<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[213857] trunk/Source</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/213857">213857</a></dd>
<dt>Author</dt> <dd>cdumez@apple.com</dd>
<dt>Date</dt> <dd>2017-03-13 11:09:30 -0700 (Mon, 13 Mar 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>Allow termination of background WebProcesses that go over a given CPU usage threshold
https://bugs.webkit.org/show_bug.cgi?id=169456
&lt;rdar://problem/30960968&gt;

Reviewed by Andreas Kling.

Source/WebCore:

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.

Source/WebKit2:

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.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreWebCorexcodeprojprojectpbxproj">trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj</a></li>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2SharedWebPageCreationParameterscpp">trunk/Source/WebKit2/Shared/WebPageCreationParameters.cpp</a></li>
<li><a href="#trunkSourceWebKit2SharedWebPageCreationParametersh">trunk/Source/WebKit2/Shared/WebPageCreationParameters.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAPIAPIPageConfigurationcpp">trunk/Source/WebKit2/UIProcess/API/APIPageConfiguration.cpp</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAPIAPIPageConfigurationh">trunk/Source/WebKit2/UIProcess/API/APIPageConfiguration.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAPICWKPageConfigurationRefcpp">trunk/Source/WebKit2/UIProcess/API/C/WKPageConfigurationRef.cpp</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAPICWKPageConfigurationRefh">trunk/Source/WebKit2/UIProcess/API/C/WKPageConfigurationRef.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessWebPageProxycpp">trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp</a></li>
<li><a href="#trunkSourceWebKit2UIProcessWebPageProxyh">trunk/Source/WebKit2/UIProcess/WebPageProxy.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessWebProcessProxycpp">trunk/Source/WebKit2/UIProcess/WebProcessProxy.cpp</a></li>
<li><a href="#trunkSourceWebKit2UIProcessWebProcessProxyh">trunk/Source/WebKit2/UIProcess/WebProcessProxy.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessWebProcessProxymessagesin">trunk/Source/WebKit2/UIProcess/WebProcessProxy.messages.in</a></li>
<li><a href="#trunkSourceWebKit2WebProcessWebPageWebPagecpp">trunk/Source/WebKit2/WebProcess/WebPage/WebPage.cpp</a></li>
<li><a href="#trunkSourceWebKit2WebProcessWebPageWebPageh">trunk/Source/WebKit2/WebProcess/WebPage/WebPage.h</a></li>
<li><a href="#trunkSourceWebKit2WebProcessWebProcesscpp">trunk/Source/WebKit2/WebProcess/WebProcess.cpp</a></li>
<li><a href="#trunkSourceWebKit2WebProcessWebProcessh">trunk/Source/WebKit2/WebProcess/WebProcess.h</a></li>
<li><a href="#trunkSourceWebKit2WebProcesscocoaWebProcessCocoamm">trunk/Source/WebKit2/WebProcess/cocoa/WebProcessCocoa.mm</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreplatformCPUMonitorcpp">trunk/Source/WebCore/platform/CPUMonitor.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformCPUMonitorh">trunk/Source/WebCore/platform/CPUMonitor.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (213856 => 213857)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -1,3 +1,22 @@
</span><ins>+2017-03-13  Chris Dumez  &lt;cdumez@apple.com&gt;
+
+        Allow termination of background WebProcesses that go over a given CPU usage threshold
+        https://bugs.webkit.org/show_bug.cgi?id=169456
+        &lt;rdar://problem/30960968&gt;
+
+        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.
+
</ins><span class="cx"> 2017-03-13  Antoine Quint  &lt;graouts@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [Modern Media Controls] Volume icon doesn't turn to mute when the knob is set to 0
</span></span></pre></div>
<a id="trunkSourceWebCoreWebCorexcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (213856 => 213857)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -1912,6 +1912,8 @@
</span><span class="cx">                 467302021C4EFE7800BCB357 /* IgnoreOpensDuringUnloadCountIncrementer.h in Headers */ = {isa = PBXBuildFile; fileRef = 467302011C4EFE6600BCB357 /* IgnoreOpensDuringUnloadCountIncrementer.h */; };
</span><span class="cx">                 4689F1AF1267BAE100E8D380 /* FileMetadata.h in Headers */ = {isa = PBXBuildFile; fileRef = 4689F1AE1267BAE100E8D380 /* FileMetadata.h */; };
</span><span class="cx">                 46B63F6C1C6E8D19002E914B /* JSEventTargetCustom.h in Headers */ = {isa = PBXBuildFile; fileRef = 46B63F6B1C6E8CDF002E914B /* JSEventTargetCustom.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><ins>+                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 */; };
</ins><span class="cx">                 46C83EFD1A9BBE2900A79A41 /* GeoNotifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 46C83EFB1A9BBE2900A79A41 /* GeoNotifier.cpp */; };
</span><span class="cx">                 46C83EFE1A9BBE2900A79A41 /* GeoNotifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 46C83EFC1A9BBE2900A79A41 /* GeoNotifier.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 46DBB6501AB8C96F00D9A813 /* PowerObserverMac.h in Headers */ = {isa = PBXBuildFile; fileRef = 46DBB64E1AB8C96F00D9A813 /* PowerObserverMac.h */; };
</span><span class="lines">@@ -9481,6 +9483,8 @@
</span><span class="cx">                 467302011C4EFE6600BCB357 /* IgnoreOpensDuringUnloadCountIncrementer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IgnoreOpensDuringUnloadCountIncrementer.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 4689F1AE1267BAE100E8D380 /* FileMetadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileMetadata.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 46B63F6B1C6E8CDF002E914B /* JSEventTargetCustom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSEventTargetCustom.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><ins>+                46C696C91E7205E400597937 /* CPUMonitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CPUMonitor.h; sourceTree = &quot;&lt;group&gt;&quot;; };
+                46C696CA1E7205E400597937 /* CPUMonitor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CPUMonitor.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</ins><span class="cx">                 46C83EFB1A9BBE2900A79A41 /* GeoNotifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GeoNotifier.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 46C83EFC1A9BBE2900A79A41 /* GeoNotifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GeoNotifier.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 46DBB64E1AB8C96F00D9A813 /* PowerObserverMac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PowerObserverMac.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="lines">@@ -23637,6 +23641,8 @@
</span><span class="cx">                                 D8B6152E1032495100C8554A /* Cookie.h */,
</span><span class="cx">                                 339B5B62131DAA3200F48D02 /* CookiesStrategy.h */,
</span><span class="cx">                                 862F129D18C1572C005C54AF /* CountedUserActivity.h */,
</span><ins>+                                46C696CA1E7205E400597937 /* CPUMonitor.cpp */,
+                                46C696C91E7205E400597937 /* CPUMonitor.h */,
</ins><span class="cx">                                 463763061E26FDBA008CD46D /* CPUTime.cpp */,
</span><span class="cx">                                 463763071E26FDBA008CD46D /* CPUTime.h */,
</span><span class="cx">                                 E11AF15011B9A1A300805103 /* Cursor.cpp */,
</span><span class="lines">@@ -29023,6 +29029,7 @@
</span><span class="cx">                                 E139866415478474001E3F65 /* StyleResolver.h in Headers */,
</span><span class="cx">                                 E4BBED4D14FCDBA1003F0B98 /* StyleRule.h in Headers */,
</span><span class="cx">                                 E4946EAF156E64DD00D3297F /* StyleRuleImport.h in Headers */,
</span><ins>+                                46C696CB1E7205F700597937 /* CPUMonitor.h in Headers */,
</ins><span class="cx">                                 E461D65F1BB0C80D00CB5645 /* StyleScope.h in Headers */,
</span><span class="cx">                                 F47A5E3E195B8C8A00483100 /* StyleScrollSnapPoints.h in Headers */,
</span><span class="cx">                                 9D6380101AF173220031A15C /* StyleSelfAlignmentData.h in Headers */,
</span><span class="lines">@@ -30262,6 +30269,7 @@
</span><span class="cx">                                 59B597731108656B007159E8 /* BridgeJSC.cpp in Sources */,
</span><span class="cx">                                 7A45032F18DB717200377B34 /* BufferedLineReader.cpp in Sources */,
</span><span class="cx">                                 F55B3DAF1251F12D003EF269 /* ButtonInputType.cpp in Sources */,
</span><ins>+                                46C696CC1E7205FC00597937 /* CPUMonitor.cpp in Sources */,
</ins><span class="cx">                                 1A569CF70D7E2B82007C3983 /* c_class.cpp in Sources */,
</span><span class="cx">                                 ECA680C91E67730B00731D20 /* StringUtilities.mm in Sources */,
</span><span class="cx">                                 1A569CF90D7E2B82007C3983 /* c_instance.cpp in Sources */,
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformCPUMonitorcpp"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/platform/CPUMonitor.cpp (0 => 213857)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/CPUMonitor.cpp                                (rev 0)
+++ trunk/Source/WebCore/platform/CPUMonitor.cpp        2017-03-13 18:09:30 UTC (rev 213857)
</span><span class="lines">@@ -0,0 +1,73 @@
</span><ins>+/*
+ * 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 &quot;config.h&quot;
+#include &quot;CPUMonitor.h&quot;
+
+namespace WebCore {
+
+CPUMonitor::CPUMonitor(Seconds checkInterval, ExceededCPULimitHandler&amp;&amp; exceededCPULimitHandler)
+    : m_checkInterval(checkInterval)
+    , m_exceededCPULimitHandler(WTFMove(exceededCPULimitHandler))
+    , m_timer(*this, &amp;CPUMonitor::timerFired)
+{
+}
+
+void CPUMonitor::setCPULimit(std::optional&lt;double&gt; 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 &gt; m_cpuLimit.value() * 100)
+        m_exceededCPULimitHandler();
+
+    m_lastCPUTime = cpuTime;
+}
+
+} // namespace WebCore
</ins></span></pre></div>
<a id="trunkSourceWebCoreplatformCPUMonitorh"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/platform/CPUMonitor.h (0 => 213857)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/CPUMonitor.h                                (rev 0)
+++ trunk/Source/WebCore/platform/CPUMonitor.h        2017-03-13 18:09:30 UTC (rev 213857)
</span><span class="lines">@@ -0,0 +1,52 @@
</span><ins>+/*
+ * 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 &quot;CPUTime.h&quot;
+#include &quot;Timer.h&quot;
+
+#include &lt;wtf/Function.h&gt;
+
+namespace WebCore {
+
+class CPUMonitor {
+public:
+    using ExceededCPULimitHandler = WTF::Function&lt;void()&gt;;
+    WEBCORE_EXPORT CPUMonitor(Seconds checkInterval, ExceededCPULimitHandler&amp;&amp;);
+
+    WEBCORE_EXPORT void setCPULimit(std::optional&lt;double&gt;);
+
+private:
+    void timerFired();
+
+    Seconds m_checkInterval;
+    ExceededCPULimitHandler m_exceededCPULimitHandler;
+    Timer m_timer;
+    std::optional&lt;double&gt; m_cpuLimit;
+    std::optional&lt;CPUTime&gt; m_lastCPUTime;
+};
+
+} // namespace WebCore
</ins></span></pre></div>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (213856 => 213857)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -1,3 +1,129 @@
</span><ins>+2017-03-13  Chris Dumez  &lt;cdumez@apple.com&gt;
+
+        Allow termination of background WebProcesses that go over a given CPU usage threshold
+        https://bugs.webkit.org/show_bug.cgi?id=169456
+        &lt;rdar://problem/30960968&gt;
+
+        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.
+
</ins><span class="cx"> 2017-03-13  Michael Saboff  &lt;msaboff@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Add iOS plumbing to WebProcess to enable JavaScriptCore configuration and logging
</span></span></pre></div>
<a id="trunkSourceWebKit2SharedWebPageCreationParameterscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/Shared/WebPageCreationParameters.cpp (213856 => 213857)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -93,6 +93,7 @@
</span><span class="cx">     encoder.encodeEnum(userInterfaceLayoutDirection);
</span><span class="cx">     encoder.encodeEnum(observedLayoutMilestones);
</span><span class="cx">     encoder &lt;&lt; overrideContentSecurityPolicy;
</span><ins>+    encoder &lt;&lt; backgroundCPULimit;
</ins><span class="cx">     encoder &lt;&lt; urlSchemeHandlers;
</span><span class="cx"> #if ENABLE(WEB_RTC)
</span><span class="cx">     encoder &lt;&lt; iceCandidateFilteringEnabled;
</span><span class="lines">@@ -222,6 +223,9 @@
</span><span class="cx">     if (!decoder.decode(parameters.overrideContentSecurityPolicy))
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><ins>+    if (!decoder.decode(parameters.backgroundCPULimit))
+        return false;
+
</ins><span class="cx">     if (!decoder.decode(parameters.urlSchemeHandlers))
</span><span class="cx">         return false;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKit2SharedWebPageCreationParametersh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/Shared/WebPageCreationParameters.h (213856 => 213857)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -146,6 +146,7 @@
</span><span class="cx">     WebCore::LayoutMilestones observedLayoutMilestones;
</span><span class="cx"> 
</span><span class="cx">     String overrideContentSecurityPolicy;
</span><ins>+    std::optional&lt;double&gt; backgroundCPULimit;
</ins><span class="cx"> 
</span><span class="cx">     HashMap&lt;String, uint64_t&gt; urlSchemeHandlers;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAPIAPIPageConfigurationcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/API/APIPageConfiguration.cpp (213856 => 213857)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -68,6 +68,7 @@
</span><span class="cx">     copy-&gt;m_alwaysRunsAtForegroundPriority = this-&gt;m_alwaysRunsAtForegroundPriority;
</span><span class="cx"> #endif
</span><span class="cx">     copy-&gt;m_initialCapitalizationEnabled = this-&gt;m_initialCapitalizationEnabled;
</span><ins>+    copy-&gt;m_backgroundCPULimit = this-&gt;m_backgroundCPULimit;
</ins><span class="cx">     copy-&gt;m_controlledByAutomation = this-&gt;m_controlledByAutomation;
</span><span class="cx">     copy-&gt;m_overrideContentSecurityPolicy = this-&gt;m_overrideContentSecurityPolicy;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAPIAPIPageConfigurationh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/API/APIPageConfiguration.h (213856 => 213857)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -93,6 +93,9 @@
</span><span class="cx">     bool initialCapitalizationEnabled() { return m_initialCapitalizationEnabled; }
</span><span class="cx">     void setInitialCapitalizationEnabled(bool initialCapitalizationEnabled) { m_initialCapitalizationEnabled = initialCapitalizationEnabled; }
</span><span class="cx"> 
</span><ins>+    std::optional&lt;double&gt; backgroundCPULimit() const { return m_backgroundCPULimit; }
+    void setBackgroundCPULimit(double cpuLimit) { m_backgroundCPULimit = cpuLimit; }
+
</ins><span class="cx">     bool waitsForPaintAfterViewDidMoveToWindow() const { return m_waitsForPaintAfterViewDidMoveToWindow; }
</span><span class="cx">     void setWaitsForPaintAfterViewDidMoveToWindow(bool shouldSynchronize) { m_waitsForPaintAfterViewDidMoveToWindow = shouldSynchronize; }
</span><span class="cx"> 
</span><span class="lines">@@ -124,6 +127,7 @@
</span><span class="cx">     bool m_initialCapitalizationEnabled = true;
</span><span class="cx">     bool m_waitsForPaintAfterViewDidMoveToWindow = true;
</span><span class="cx">     bool m_controlledByAutomation = false;
</span><ins>+    std::optional&lt;double&gt; m_backgroundCPULimit;
</ins><span class="cx"> 
</span><span class="cx">     WTF::String m_overrideContentSecurityPolicy;
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAPICWKPageConfigurationRefcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/API/C/WKPageConfigurationRef.cpp (213856 => 213857)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -108,3 +108,8 @@
</span><span class="cx"> {
</span><span class="cx">     toImpl(configuration)-&gt;setInitialCapitalizationEnabled(enabled);
</span><span class="cx"> }
</span><ins>+
+void WKPageConfigurationSetBackgroundCPULimit(WKPageConfigurationRef configuration, double cpuLimit)
+{
+    toImpl(configuration)-&gt;setBackgroundCPULimit(cpuLimit);
+}
</ins></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAPICWKPageConfigurationRefh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/API/C/WKPageConfigurationRef.h (213856 => 213857)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -55,6 +55,7 @@
</span><span class="cx"> WK_EXPORT void WKPageConfigurationSetWebsiteDataStore(WKPageConfigurationRef configuration, WKWebsiteDataStoreRef websiteDataStore);
</span><span class="cx"> 
</span><span class="cx"> WK_EXPORT void WKPageConfigurationSetInitialCapitalizationEnabled(WKPageConfigurationRef configuration, bool enabled);
</span><ins>+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.
</ins><span class="cx"> 
</span><span class="cx"> #ifdef __cplusplus
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessWebPageProxycpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp (213856 => 213857)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -356,6 +356,7 @@
</span><span class="cx">     , m_alwaysRunsAtForegroundPriority(m_configuration-&gt;alwaysRunsAtForegroundPriority())
</span><span class="cx"> #endif
</span><span class="cx">     , m_initialCapitalizationEnabled(m_configuration-&gt;initialCapitalizationEnabled())
</span><ins>+    , m_backgroundCPULimit(m_configuration-&gt;backgroundCPULimit())
</ins><span class="cx">     , m_backForwardList(WebBackForwardList::create(*this))
</span><span class="cx">     , m_maintainsInactiveSelection(false)
</span><span class="cx">     , m_waitsForPaintAfterViewDidMoveToWindow(m_configuration-&gt;waitsForPaintAfterViewDidMoveToWindow())
</span><span class="lines">@@ -716,6 +717,7 @@
</span><span class="cx">     ASSERT(m_process-&gt;state() == WebProcessProxy::State::Terminated);
</span><span class="cx"> 
</span><span class="cx">     m_isValid = true;
</span><ins>+    m_wasTerminatedDueToResourceExhaustionWhileInBackground = false;
</ins><span class="cx">     m_process-&gt;removeWebPage(m_pageID);
</span><span class="cx">     m_process-&gt;removeMessageReceiver(Messages::WebPageProxy::messageReceiverName(), m_pageID);
</span><span class="cx"> 
</span><span class="lines">@@ -1528,8 +1530,13 @@
</span><span class="cx">     m_activityStateChangeDispatcher-&gt;invalidate();
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><del>-    if (!isValid())
</del><ins>+    if (!isValid()) {
+        if (m_potentiallyChangedActivityStateFlags &amp; ActivityState::IsVisible &amp;&amp; m_wasTerminatedDueToResourceExhaustionWhileInBackground) {
+            m_wasTerminatedDueToResourceExhaustionWhileInBackground = false;
+            processDidCrash();
+        }
</ins><span class="cx">         return;
</span><ins>+    }
</ins><span class="cx"> 
</span><span class="cx">     // If the visibility state may have changed, then so may the visually idle &amp; occluded agnostic state.
</span><span class="cx">     if (m_potentiallyChangedActivityStateFlags &amp; ActivityState::IsVisible)
</span><span class="lines">@@ -2383,7 +2390,7 @@
</span><span class="cx">     m_process-&gt;send(Messages::WebPage::SetCustomTextEncodingName(encodingName), m_pageID);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void WebPageProxy::terminateProcess()
</del><ins>+void WebPageProxy::terminateProcess(TerminationReason terminationReason)
</ins><span class="cx"> {
</span><span class="cx">     // NOTE: This uses a check of m_isValid rather than calling isValid() since
</span><span class="cx">     // we want this to run even for pages being closed or that already closed.
</span><span class="lines">@@ -2392,6 +2399,7 @@
</span><span class="cx"> 
</span><span class="cx">     m_process-&gt;requestTermination();
</span><span class="cx">     resetStateAfterProcessExited();
</span><ins>+    m_wasTerminatedDueToResourceExhaustionWhileInBackground = terminationReason == TerminationReason::ResourceExhaustionWhileInBackground;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> SessionState WebPageProxy::sessionState(const std::function&lt;bool (WebBackForwardListItem&amp;)&gt;&amp; filter) const
</span><span class="lines">@@ -5589,6 +5597,7 @@
</span><span class="cx">     parameters.userInterfaceLayoutDirection = m_pageClient.userInterfaceLayoutDirection();
</span><span class="cx">     parameters.observedLayoutMilestones = m_observedLayoutMilestones;
</span><span class="cx">     parameters.overrideContentSecurityPolicy = m_overrideContentSecurityPolicy;
</span><ins>+    parameters.backgroundCPULimit = m_backgroundCPULimit;
</ins><span class="cx"> 
</span><span class="cx">     for (auto&amp; iterator : m_urlSchemeHandlersByScheme)
</span><span class="cx">         parameters.urlSchemeHandlers.set(iterator.key, iterator.value-&gt;identifier());
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessWebPageProxyh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/WebPageProxy.h (213856 => 213857)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -680,7 +680,8 @@
</span><span class="cx"> 
</span><span class="cx">     double estimatedProgress() const;
</span><span class="cx"> 
</span><del>-    void terminateProcess();
</del><ins>+    enum class TerminationReason { ResourceExhaustionWhileInBackground, Other };
+    void terminateProcess(TerminationReason = TerminationReason::Other);
</ins><span class="cx"> 
</span><span class="cx">     SessionState sessionState(const std::function&lt;bool (WebBackForwardListItem&amp;)&gt;&amp; = nullptr) const;
</span><span class="cx">     RefPtr&lt;API::Navigation&gt; restoreFromSessionState(SessionState, bool navigate);
</span><span class="lines">@@ -1721,6 +1722,7 @@
</span><span class="cx">     ProcessThrottler::ForegroundActivityToken m_activityToken;
</span><span class="cx"> #endif
</span><span class="cx">     bool m_initialCapitalizationEnabled;
</span><ins>+    std::optional&lt;double&gt; m_backgroundCPULimit;
</ins><span class="cx">     Ref&lt;WebBackForwardList&gt; m_backForwardList;
</span><span class="cx">         
</span><span class="cx">     bool m_maintainsInactiveSelection;
</span><span class="lines">@@ -1984,6 +1986,7 @@
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx">     bool m_isUsingHighPerformanceWebGL { false };
</span><ins>+    bool m_wasTerminatedDueToResourceExhaustionWhileInBackground { false };
</ins><span class="cx">         
</span><span class="cx">     WeakPtrFactory&lt;WebPageProxy&gt; m_weakPtrFactory;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessWebProcessProxycpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/WebProcessProxy.cpp (213856 => 213857)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -1113,6 +1113,33 @@
</span><span class="cx">     m_backgroundResponsivenessTimer.processTerminated();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static Vector&lt;RefPtr&lt;WebPageProxy&gt;&gt; pagesCopy(WTF::IteratorRange&lt;WebProcessProxy::WebPageProxyMap::const_iterator::Values&gt; pages)
+{
+    Vector&lt;RefPtr&lt;WebPageProxy&gt;&gt; vector;
+    for (auto&amp; page : pages)
+        vector.append(page);
+    return vector;
+}
+
+void WebProcessProxy::didExceedBackgroundCPULimit()
+{
+    for (auto&amp; page : pages()) {
+        if (page-&gt;isViewVisible())
+            return;
+
+        if (page-&gt;isPlayingAudio()) {
+            RELEASE_LOG(PerformanceLogging, &quot;%p - WebProcessProxy::didExceedBackgroundCPULimit() WebProcess has exceeded the background CPU limit but we are not terminating it because there is audio playing&quot;, this);
+            return;
+        }
+    }
+
+    RELEASE_LOG(PerformanceLogging, &quot;%p - WebProcessProxy::didExceedBackgroundCPULimit() Terminating background WebProcess that has exceeded the background CPU limit&quot;, this);
+
+    // We only terminate the process here. We will call processDidCrash() once one of the pages becomes visible again (see WebPageProxy::dispatchActivityStateChange()).
+    for (auto&amp; page : pagesCopy(pages()))
+        page-&gt;terminateProcess(WebPageProxy::TerminationReason::ResourceExhaustionWhileInBackground);
+}
+
</ins><span class="cx"> void WebProcessProxy::updateBackgroundResponsivenessTimer()
</span><span class="cx"> {
</span><span class="cx">     m_backgroundResponsivenessTimer.updateState();
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessWebProcessProxyh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/WebProcessProxy.h (213856 => 213857)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -164,6 +164,7 @@
</span><span class="cx">     bool isUnderMemoryPressure() const { return m_isUnderMemoryPressure; }
</span><span class="cx"> 
</span><span class="cx">     void processTerminated();
</span><ins>+    void didExceedBackgroundCPULimit();
</ins><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     explicit WebProcessProxy(WebProcessPool&amp;, WebsiteDataStore*);
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessWebProcessProxymessagesin"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/WebProcessProxy.messages.in (213856 => 213857)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -45,6 +45,8 @@
</span><span class="cx"> 
</span><span class="cx">     SetIsHoldingLockedFiles(bool isHoldingLockedFiles)
</span><span class="cx"> 
</span><ins>+    DidExceedBackgroundCPULimit()
+
</ins><span class="cx">     RetainIconForPageURL(String pageURL)
</span><span class="cx">     ReleaseIconForPageURL(String pageURL)
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKit2WebProcessWebPageWebPagecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/WebProcess/WebPage/WebPage.cpp (213856 => 213857)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -361,6 +361,7 @@
</span><span class="cx">     , m_userActivityHysteresis([this](HysteresisState) { updateUserActivity(); })
</span><span class="cx">     , m_userInterfaceLayoutDirection(parameters.userInterfaceLayoutDirection)
</span><span class="cx">     , m_overrideContentSecurityPolicy { parameters.overrideContentSecurityPolicy }
</span><ins>+    , m_backgroundCPULimit(parameters.backgroundCPULimit)
</ins><span class="cx"> {
</span><span class="cx">     ASSERT(m_pageID);
</span><span class="cx"> 
</span><span class="lines">@@ -2624,6 +2625,7 @@
</span><span class="cx">         pluginView-&gt;activityStateDidChange(changed);
</span><span class="cx"> 
</span><span class="cx">     m_drawingArea-&gt;activityStateDidChange(changed, wantsDidUpdateActivityState, callbackIDs);
</span><ins>+    WebProcess::singleton().pageActivityStateDidChange(m_pageID, changed);
</ins><span class="cx"> 
</span><span class="cx">     if (changed &amp; ActivityState::IsInWindow)
</span><span class="cx">         updateIsInWindow();
</span></span></pre></div>
<a id="trunkSourceWebKit2WebProcessWebPageWebPageh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/WebProcess/WebPage/WebPage.h (213856 => 213857)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -972,6 +972,7 @@
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx">     WebURLSchemeHandlerProxy* urlSchemeHandlerForScheme(const String&amp;);
</span><ins>+    std::optional&lt;double&gt; backgroundCPULimit() const { return m_backgroundCPULimit; }
</ins><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     WebPage(uint64_t pageID, WebPageCreationParameters&amp;&amp;);
</span><span class="lines">@@ -1550,6 +1551,7 @@
</span><span class="cx">     WebCore::UserInterfaceLayoutDirection m_userInterfaceLayoutDirection { WebCore::UserInterfaceLayoutDirection::LTR };
</span><span class="cx"> 
</span><span class="cx">     const String m_overrideContentSecurityPolicy;
</span><ins>+    const std::optional&lt;double&gt; m_backgroundCPULimit;
</ins><span class="cx"> 
</span><span class="cx">     HashMap&lt;String, std::unique_ptr&lt;WebURLSchemeHandlerProxy&gt;&gt; m_schemeToURLSchemeHandlerProxyMap;
</span><span class="cx">     HashMap&lt;uint64_t, WebURLSchemeHandlerProxy*&gt; m_identifierToURLSchemeHandlerProxyMap;
</span></span></pre></div>
<a id="trunkSourceWebKit2WebProcessWebProcesscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/WebProcess/WebProcess.cpp (213856 => 213857)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -71,6 +71,7 @@
</span><span class="cx"> #include &lt;WebCore/AXObjectCache.h&gt;
</span><span class="cx"> #include &lt;WebCore/ApplicationCacheStorage.h&gt;
</span><span class="cx"> #include &lt;WebCore/AuthenticationChallenge.h&gt;
</span><ins>+#include &lt;WebCore/CPUMonitor.h&gt;
</ins><span class="cx"> #include &lt;WebCore/CommonVM.h&gt;
</span><span class="cx"> #include &lt;WebCore/CrossOriginPreflightResultCache.h&gt;
</span><span class="cx"> #include &lt;WebCore/DNS.h&gt;
</span><span class="lines">@@ -580,6 +581,7 @@
</span><span class="cx"> 
</span><span class="cx">         // Balanced by an enableTermination in removeWebPage.
</span><span class="cx">         disableTermination();
</span><ins>+        updateBackgroundCPULimit();
</ins><span class="cx">     } else
</span><span class="cx">         result.iterator-&gt;value-&gt;reinitializeWebPage(WTFMove(parameters));
</span><span class="cx"> 
</span><span class="lines">@@ -594,6 +596,7 @@
</span><span class="cx">     m_pageMap.remove(pageID);
</span><span class="cx"> 
</span><span class="cx">     enableTermination();
</span><ins>+    updateBackgroundCPULimit();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool WebProcess::shouldTerminate()
</span><span class="lines">@@ -1268,8 +1271,22 @@
</span><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void WebProcess::updateBackgroundCPULimit()
+{
+}
+
+void WebProcess::updateBackgroundCPUMonitorState()
+{
+}
+
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+void WebProcess::pageActivityStateDidChange(uint64_t, WebCore::ActivityState::Flags changed)
+{
+    if (changed &amp; WebCore::ActivityState::IsVisible)
+        updateBackgroundCPUMonitorState();
+}
+
</ins><span class="cx"> #if PLATFORM(IOS)
</span><span class="cx"> void WebProcess::resetAllGeolocationPermissions()
</span><span class="cx"> {
</span><span class="lines">@@ -1561,6 +1578,15 @@
</span><span class="cx">     m_dnsPrefetchHystereris.impulse();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+bool WebProcess::hasVisibleWebPage() const
+{
+    for (auto&amp; page : m_pageMap.values()) {
+        if (page-&gt;isVisible())
+            return true;
+    }
+    return false;
+}
+
</ins><span class="cx"> #if USE(LIBWEBRTC)
</span><span class="cx"> LibWebRTCNetwork&amp; WebProcess::libWebRTCNetwork()
</span><span class="cx"> {
</span></span></pre></div>
<a id="trunkSourceWebKit2WebProcessWebProcessh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/WebProcess/WebProcess.h (213856 => 213857)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -62,6 +62,7 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> class ApplicationCacheStorage;
</span><ins>+class CPUMonitor;
</ins><span class="cx"> class CertificateInfo;
</span><span class="cx"> class PageGroup;
</span><span class="cx"> class ResourceRequest;
</span><span class="lines">@@ -194,6 +195,7 @@
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx">     void updateActivePages();
</span><ins>+    void pageActivityStateDidChange(uint64_t pageID, WebCore::ActivityState::Flags changed);
</ins><span class="cx"> 
</span><span class="cx">     void setHiddenPageDOMTimerThrottlingIncreaseLimit(int milliseconds);
</span><span class="cx"> 
</span><span class="lines">@@ -318,6 +320,9 @@
</span><span class="cx">     void destroyAutomationSessionProxy();
</span><span class="cx"> 
</span><span class="cx">     void logDiagnosticMessageForNetworkProcessCrash();
</span><ins>+    bool hasVisibleWebPage() const;
+    void updateBackgroundCPULimit();
+    void updateBackgroundCPUMonitorState();
</ins><span class="cx"> 
</span><span class="cx">     // ChildProcess
</span><span class="cx">     void initializeProcess(const ChildProcessInitializationParameters&amp;) override;
</span><span class="lines">@@ -419,6 +424,10 @@
</span><span class="cx"> 
</span><span class="cx">     unsigned m_pagesMarkingLayersAsVolatile { 0 };
</span><span class="cx">     bool m_suppressMemoryPressureHandler { false };
</span><ins>+#if PLATFORM(MAC)
+    std::unique_ptr&lt;WebCore::CPUMonitor&gt; m_backgroundCPUMonitor;
+    std::optional&lt;double&gt; m_backgroundCPULimit;
+#endif
</ins><span class="cx"> 
</span><span class="cx">     HashMap&lt;WebCore::UserGestureToken *, uint64_t&gt; m_userGestureTokens;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKit2WebProcesscocoaWebProcessCocoamm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/WebProcess/cocoa/WebProcessCocoa.mm (213856 => 213857)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -48,6 +48,7 @@
</span><span class="cx"> #import &lt;JavaScriptCore/Options.h&gt;
</span><span class="cx"> #import &lt;WebCore/AXObjectCache.h&gt;
</span><span class="cx"> #import &lt;WebCore/CFNetworkSPI.h&gt;
</span><ins>+#import &lt;WebCore/CPUMonitor.h&gt;
</ins><span class="cx"> #import &lt;WebCore/FileSystem.h&gt;
</span><span class="cx"> #import &lt;WebCore/FontCache.h&gt;
</span><span class="cx"> #import &lt;WebCore/FontCascade.h&gt;
</span><span class="lines">@@ -77,6 +78,10 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebKit {
</span><span class="cx"> 
</span><ins>+#if PLATFORM(MAC)
+static const Seconds backgroundCPUMonitoringInterval { 15_min };
+#endif
+
</ins><span class="cx"> void WebProcess::platformSetCacheModel(CacheModel)
</span><span class="cx"> {
</span><span class="cx"> }
</span><span class="lines">@@ -369,6 +374,49 @@
</span><span class="cx"> #endif
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void WebProcess::updateBackgroundCPULimit()
+{
+#if PLATFORM(MAC)
+    std::optional&lt;double&gt; backgroundCPULimit;
+
+    // Use the largest limit among all pages in this process.
+    for (auto&amp; page : m_pageMap.values()) {
+        auto pageCPULimit = page-&gt;backgroundCPULimit();
+        if (!pageCPULimit) {
+            backgroundCPULimit = std::nullopt;
+            break;
+        }
+        if (!backgroundCPULimit || pageCPULimit &gt; 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-&gt;setCPULimit(std::nullopt);
+        return;
+    }
+
+    if (!m_backgroundCPUMonitor) {
+        m_backgroundCPUMonitor = std::make_unique&lt;CPUMonitor&gt;(backgroundCPUMonitoringInterval, [this] {
+            RELEASE_LOG(PerformanceLogging, &quot;%p - WebProcess exceeded background CPU limit of %.1f%%&quot;, this, m_backgroundCPULimit.value() * 100);
+            parentProcessConnection()-&gt;send(Messages::WebProcessProxy::DidExceedBackgroundCPULimit(), 0);
+        });
+    }
+    m_backgroundCPUMonitor-&gt;setCPULimit(m_backgroundCPULimit.value());
+#endif
+}
+
</ins><span class="cx"> RefPtr&lt;ObjCObjectGraph&gt; WebProcess::transformHandlesToObjects(ObjCObjectGraph&amp; objectGraph)
</span><span class="cx"> {
</span><span class="cx">     struct Transformer final : ObjCObjectGraph::Transformer {
</span></span></pre>
</div>
</div>

</body>
</html>