WebKit Bugzilla
Attachment 342390 Details for
Bug 186468
: Reload the Web view in case of crash if the client does not implement webViewWebContentProcessDidTerminate delegate
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-186468-20180610142631.patch (text/plain), 19.74 KB, created by
Chris Dumez
on 2018-06-10 14:26:33 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Chris Dumez
Created:
2018-06-10 14:26:33 PDT
Size:
19.74 KB
patch
obsolete
>Subversion Revision: 232660 >diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog >index 198d70cbf298ca02078cffa80f910e1f2346b417..9ab1c17e6680bae74069076f48195b5b495661ec 100644 >--- a/Source/WebKit/ChangeLog >+++ b/Source/WebKit/ChangeLog >@@ -1,3 +1,37 @@ >+2018-06-10 Chris Dumez <cdumez@apple.com> >+ >+ Reload the Web view in case of crash if the client does not implement webViewWebContentProcessDidTerminate delegate >+ https://bugs.webkit.org/show_bug.cgi?id=186468 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ We now attempt to reload the Web view if the web content process crashes and the client >+ does not implement the webViewWebContentProcessDidTerminate delegate (or any of the similar >+ delegates in our SPI). >+ >+ * UIProcess/API/APILoaderClient.h: >+ (API::LoaderClient::processDidCrash): >+ * UIProcess/API/APINavigationClient.h: >+ (API::NavigationClient::processDidTerminate): >+ * UIProcess/API/C/WKPage.cpp: >+ (WKPageSetPageLoaderClient): >+ (WKPageSetPageNavigationClient): >+ * UIProcess/API/Cocoa/WKWebViewPrivate.h: >+ * UIProcess/API/glib/WebKitNavigationClient.cpp: >+ * UIProcess/Cocoa/NavigationState.h: >+ * UIProcess/Cocoa/NavigationState.mm: >+ (WebKit::NavigationState::NavigationClient::processDidTerminate): >+ * UIProcess/WebPageProxy.cpp: >+ (WebKit::m_resetRecentCrashCountTimer): >+ (WebKit::WebPageProxy::didFinishLoadForFrame): >+ (WebKit::shouldReloadAfterProcessTermination): >+ (WebKit::WebPageProxy::dispatchProcessDidTerminate): >+ (WebKit::WebPageProxy::tryReloadAfterProcessTermination): >+ (WebKit::WebPageProxy::resetRecentCrashCountSoon): >+ (WebKit::WebPageProxy::resetRecentCrashCount): >+ (WebKit::m_configurationPreferenceValues): Deleted. >+ * UIProcess/WebPageProxy.h: >+ > 2018-06-08 Wenson Hsieh <wenson_hsieh@apple.com> > > [WebKit on watchOS] Upstream watchOS source additions to OpenSource (Part 1) >diff --git a/Source/WebKit/UIProcess/API/APILoaderClient.h b/Source/WebKit/UIProcess/API/APILoaderClient.h >index 38410da025dcb671fb0c89105f2aaa394652ff5f..29e8c945572511c7bf3404fb263cf0f9d3dbe538 100644 >--- a/Source/WebKit/UIProcess/API/APILoaderClient.h >+++ b/Source/WebKit/UIProcess/API/APILoaderClient.h >@@ -87,7 +87,7 @@ public: > // FIXME: These functions should not be part of this client. > virtual void processDidBecomeUnresponsive(WebKit::WebPageProxy&) { } > virtual void processDidBecomeResponsive(WebKit::WebPageProxy&) { } >- virtual void processDidCrash(WebKit::WebPageProxy&) { } >+ virtual bool processDidCrash(WebKit::WebPageProxy&) { return false; } > > virtual void didChangeBackForwardList(WebKit::WebPageProxy&, WebKit::WebBackForwardListItem*, Vector<Ref<WebKit::WebBackForwardListItem>>&&) { } > virtual bool shouldKeepCurrentBackForwardListItemInList(WebKit::WebPageProxy&, WebKit::WebBackForwardListItem&) { return true; } >diff --git a/Source/WebKit/UIProcess/API/APINavigationClient.h b/Source/WebKit/UIProcess/API/APINavigationClient.h >index 470f65feaadb71e11e35204a3327730e883a6e32..46e7d902dbe52d5df307373835324e6c6c934882 100644 >--- a/Source/WebKit/UIProcess/API/APINavigationClient.h >+++ b/Source/WebKit/UIProcess/API/APINavigationClient.h >@@ -89,7 +89,7 @@ public: > virtual void didReceiveAuthenticationChallenge(WebKit::WebPageProxy&, WebKit::AuthenticationChallengeProxy&) { } > > // FIXME: These function should not be part of this client. >- virtual void processDidTerminate(WebKit::WebPageProxy&, WebKit::ProcessTerminationReason) { } >+ virtual bool processDidTerminate(WebKit::WebPageProxy&, WebKit::ProcessTerminationReason) { return false; } > virtual void processDidBecomeResponsive(WebKit::WebPageProxy&) { } > virtual void processDidBecomeUnresponsive(WebKit::WebPageProxy&) { } > >diff --git a/Source/WebKit/UIProcess/API/C/WKPage.cpp b/Source/WebKit/UIProcess/API/C/WKPage.cpp >index 58647c7471c3471dcee08ac2701f9bc51e1aa561..78c5e1d7f09c7757e5dff31cb1fae8a8f9fdc5d0 100644 >--- a/Source/WebKit/UIProcess/API/C/WKPage.cpp >+++ b/Source/WebKit/UIProcess/API/C/WKPage.cpp >@@ -1198,12 +1198,13 @@ void WKPageSetPageLoaderClient(WKPageRef pageRef, const WKPageLoaderClientBase* > m_client.processDidBecomeResponsive(toAPI(&page), m_client.base.clientInfo); > } > >- void processDidCrash(WebPageProxy& page) override >+ bool processDidCrash(WebPageProxy& page) override > { > if (!m_client.processDidCrash) >- return; >+ return false; > > m_client.processDidCrash(toAPI(&page), m_client.base.clientInfo); >+ return true; > } > > void didChangeBackForwardList(WebPageProxy& page, WebBackForwardListItem* addedItem, Vector<Ref<WebBackForwardListItem>>&& removedItems) override >@@ -2299,15 +2300,19 @@ void WKPageSetPageNavigationClient(WKPageRef pageRef, const WKPageNavigationClie > m_client.didReceiveAuthenticationChallenge(toAPI(&page), toAPI(&authenticationChallenge), m_client.base.clientInfo); > } > >- void processDidTerminate(WebPageProxy& page, WebKit::ProcessTerminationReason reason) override >+ bool processDidTerminate(WebPageProxy& page, WebKit::ProcessTerminationReason reason) override > { > if (m_client.webProcessDidTerminate) { > m_client.webProcessDidTerminate(toAPI(&page), toAPI(reason), m_client.base.clientInfo); >- return; >+ return true; > } > >- if (m_client.webProcessDidCrash && reason != WebKit::ProcessTerminationReason::RequestedByClient) >+ if (m_client.webProcessDidCrash && reason != WebKit::ProcessTerminationReason::RequestedByClient) { > m_client.webProcessDidCrash(toAPI(&page), m_client.base.clientInfo); >+ return true; >+ } >+ >+ return false; > } > > RefPtr<API::Data> webCryptoMasterKey(WebPageProxy& page) override >diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h b/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h >index a5721dbd373b6861f954cb17c77efacfec887cc0..18ac6b861665b4716350cd2d979d41cd006e2b6e 100644 >--- a/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h >+++ b/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h >@@ -234,8 +234,6 @@ typedef NS_OPTIONS(NSUInteger, _WKRectEdge) { > > - (void)_setOverlaidAccessoryViewsInset:(CGSize)inset; > >-- (void)_killWebContentProcess; >- > // Puts the view into a state where being taken out of the view hierarchy and resigning first responder > // will not count as becoming inactive and unfocused. The returned block must be called to exit the state. > - (void (^)(void))_retainActiveFocusedState WK_API_AVAILABLE(ios(9_0)); >@@ -304,6 +302,7 @@ typedef NS_OPTIONS(NSUInteger, _WKRectEdge) { > - (WKNavigation *)_reloadWithoutContentBlockers WK_API_AVAILABLE(macosx(10.12), ios(10.0)); > - (WKNavigation *)_reloadExpiredOnly WK_API_AVAILABLE(macosx(10.13), ios(11.0)); > >+- (void)_killWebContentProcess; > - (void)_killWebContentProcessAndResetState; > > - (void)_getMainResourceDataWithCompletionHandler:(void (^)(NSData *, NSError *))completionHandler; >diff --git a/Source/WebKit/UIProcess/API/glib/WebKitNavigationClient.cpp b/Source/WebKit/UIProcess/API/glib/WebKitNavigationClient.cpp >index 4329b5d5b805cad12831ea4fc372daea654c84a9..842b97408e2f4d8351d0bb4790577dfcf760c650 100644 >--- a/Source/WebKit/UIProcess/API/glib/WebKitNavigationClient.cpp >+++ b/Source/WebKit/UIProcess/API/glib/WebKitNavigationClient.cpp >@@ -105,20 +105,21 @@ private: > webkitWebViewHandleAuthenticationChallenge(m_webView, &authenticationChallenge); > } > >- void processDidTerminate(WebPageProxy&, ProcessTerminationReason reason) override >+ bool processDidTerminate(WebPageProxy&, ProcessTerminationReason reason) override > { > switch (reason) { > case ProcessTerminationReason::Crash: > webkitWebViewWebProcessTerminated(m_webView, WEBKIT_WEB_PROCESS_CRASHED); >- break; >+ return true; > case ProcessTerminationReason::ExceededMemoryLimit: > webkitWebViewWebProcessTerminated(m_webView, WEBKIT_WEB_PROCESS_EXCEEDED_MEMORY_LIMIT); >- break; >+ return true; > case ProcessTerminationReason::ExceededCPULimit: > case ProcessTerminationReason::RequestedByClient: > case ProcessTerminationReason::NavigationSwap: > break; > } >+ return false; > } > > void decidePolicyForNavigationAction(WebPageProxy&, Ref<API::NavigationAction>&& navigationAction, Ref<WebFramePolicyListenerProxy>&& listener, API::Object* /* userData */) override >diff --git a/Source/WebKit/UIProcess/Cocoa/NavigationState.h b/Source/WebKit/UIProcess/Cocoa/NavigationState.h >index f8f2855949f2d72e922dd9c5ae44f84a546776eb..2bfe65c8beb1130cc5300ab6e252998fe3d1eb03 100644 >--- a/Source/WebKit/UIProcess/Cocoa/NavigationState.h >+++ b/Source/WebKit/UIProcess/Cocoa/NavigationState.h >@@ -110,7 +110,7 @@ private: > > bool canAuthenticateAgainstProtectionSpace(WebPageProxy&, WebProtectionSpace*) override; > void didReceiveAuthenticationChallenge(WebPageProxy&, AuthenticationChallengeProxy&) override; >- void processDidTerminate(WebPageProxy&, ProcessTerminationReason) override; >+ bool processDidTerminate(WebPageProxy&, ProcessTerminationReason) override; > void processDidBecomeResponsive(WebPageProxy&) override; > void processDidBecomeUnresponsive(WebPageProxy&) override; > >diff --git a/Source/WebKit/UIProcess/Cocoa/NavigationState.mm b/Source/WebKit/UIProcess/Cocoa/NavigationState.mm >index 8c3fa9d7496026b0b631aa7a11f9c2fa1287e0bb..6f07ee13b6dc4e349be557f7bcd1bea1b2d84987 100644 >--- a/Source/WebKit/UIProcess/Cocoa/NavigationState.mm >+++ b/Source/WebKit/UIProcess/Cocoa/NavigationState.mm >@@ -977,30 +977,31 @@ static _WKProcessTerminationReason wkProcessTerminationReason(ProcessTermination > return _WKProcessTerminationReasonCrash; > } > >-void NavigationState::NavigationClient::processDidTerminate(WebPageProxy& page, ProcessTerminationReason reason) >+bool NavigationState::NavigationClient::processDidTerminate(WebPageProxy& page, ProcessTerminationReason reason) > { > if (!m_navigationState.m_navigationDelegateMethods.webViewWebContentProcessDidTerminate > && !m_navigationState.m_navigationDelegateMethods.webViewWebContentProcessDidTerminateWithReason > && !m_navigationState.m_navigationDelegateMethods.webViewWebProcessDidCrash) >- return; >+ return false; > > auto navigationDelegate = m_navigationState.m_navigationDelegate.get(); > if (!navigationDelegate) >- return; >+ return false; > > if (m_navigationState.m_navigationDelegateMethods.webViewWebContentProcessDidTerminateWithReason) { > [static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webView:m_navigationState.m_webView webContentProcessDidTerminateWithReason:wkProcessTerminationReason(reason)]; >- return; >+ return true; > } > > // We prefer webViewWebContentProcessDidTerminate: over _webViewWebProcessDidCrash:. > if (m_navigationState.m_navigationDelegateMethods.webViewWebContentProcessDidTerminate) { > [navigationDelegate webViewWebContentProcessDidTerminate:m_navigationState.m_webView]; >- return; >+ return true; > } > > ASSERT(m_navigationState.m_navigationDelegateMethods.webViewWebProcessDidCrash); > [static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webViewWebProcessDidCrash:m_navigationState.m_webView]; >+ return true; > } > > void NavigationState::NavigationClient::processDidBecomeResponsive(WebPageProxy& page) >diff --git a/Source/WebKit/UIProcess/WebPageProxy.cpp b/Source/WebKit/UIProcess/WebPageProxy.cpp >index 4dd0aec8d8c6fe1f0cda377f2fe6c9d3632292d2..7ef0c2d07fc1646df3da39767659877fb6e40b41 100644 >--- a/Source/WebKit/UIProcess/WebPageProxy.cpp >+++ b/Source/WebKit/UIProcess/WebPageProxy.cpp >@@ -227,6 +227,9 @@ using namespace WebCore; > // Represents the number of wheel events we can hold in the queue before we start pushing them preemptively. > static const unsigned wheelEventQueueSizeThreshold = 10; > >+static const Seconds resetRecentCrashCountDelay = 30_s; >+static unsigned maximumWebProcessRelaunchAttempts = 1; >+ > namespace WebKit { > > DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, webPageProxyCounter, ("WebPageProxy")); >@@ -408,6 +411,7 @@ WebPageProxy::WebPageProxy(PageClient& pageClient, WebProcessProxy& process, uin > #endif > , m_pageLoadState(*this) > , m_configurationPreferenceValues(m_configuration->preferenceValues()) >+ , m_resetRecentCrashCountTimer(RunLoop::main(), this, &WebPageProxy::resetRecentCrashCount) > { > m_webProcessLifetimeTracker.addObserver(m_visitedLinkStore); > m_webProcessLifetimeTracker.addObserver(m_websiteDataStore); >@@ -3743,6 +3747,8 @@ void WebPageProxy::didFinishLoadForFrame(uint64_t frameID, uint64_t navigationID > if (isMainFrame) { > reportPageLoadResult(); > m_pageClient.didFinishLoadForMainFrame(); >+ >+ resetRecentCrashCountSoon(); > } > > m_isLoadingAlternateHTMLStringForFailingProvisionalLoad = false; >@@ -5853,12 +5859,53 @@ void WebPageProxy::processDidTerminate(ProcessTerminationReason reason) > stopAllURLSchemeTasks(); > } > >+static bool shouldReloadAfterProcessTermination(ProcessTerminationReason reason) >+{ >+ switch (reason) { >+ case ProcessTerminationReason::ExceededMemoryLimit: >+ case ProcessTerminationReason::ExceededCPULimit: >+ case ProcessTerminationReason::Crash: >+ return true; >+ case ProcessTerminationReason::NavigationSwap: >+ case ProcessTerminationReason::RequestedByClient: >+ break; >+ } >+ return false; >+} >+ > void WebPageProxy::dispatchProcessDidTerminate(ProcessTerminationReason reason) > { >+ bool handledByClient = false; > if (m_navigationClient) >- m_navigationClient->processDidTerminate(*this, reason); >+ handledByClient = m_navigationClient->processDidTerminate(*this, reason); > else if (reason != ProcessTerminationReason::RequestedByClient) >- m_loaderClient->processDidCrash(*this); >+ handledByClient = m_loaderClient->processDidCrash(*this); >+ >+ if (!handledByClient && shouldReloadAfterProcessTermination(reason)) >+ tryReloadAfterProcessTermination(); >+} >+ >+void WebPageProxy::tryReloadAfterProcessTermination() >+{ >+ m_resetRecentCrashCountTimer.stop(); >+ >+ if (++m_recentCrashCount > maximumWebProcessRelaunchAttempts) { >+ RELEASE_LOG_IF_ALLOWED(Process, "%p - WebPageProxy's process crashed and the client did not handle it, not reloading the page because we reached the maximum number of attempts", this); >+ m_recentCrashCount = 0; >+ return; >+ } >+ RELEASE_LOG_IF_ALLOWED(Process, "%p - WebPageProxy's process crashed and the client did not handle it, reloading the page", this); >+ reload(ReloadOption::ExpiredOnly); >+} >+ >+void WebPageProxy::resetRecentCrashCountSoon() >+{ >+ m_resetRecentCrashCountTimer.startOneShot(resetRecentCrashCountDelay); >+} >+ >+void WebPageProxy::resetRecentCrashCount() >+{ >+ m_recentCrashCount = 0; > } > > void WebPageProxy::stopAllURLSchemeTasks() >diff --git a/Source/WebKit/UIProcess/WebPageProxy.h b/Source/WebKit/UIProcess/WebPageProxy.h >index 2b62a389c41375612391c93e8603322d112175f8..e27df387c45fbc6666a50446352d10f4a63c8f99 100644 >--- a/Source/WebKit/UIProcess/WebPageProxy.h >+++ b/Source/WebKit/UIProcess/WebPageProxy.h >@@ -92,6 +92,7 @@ > #include <wtf/ProcessID.h> > #include <wtf/Ref.h> > #include <wtf/RefPtr.h> >+#include <wtf/RunLoop.h> > #include <wtf/Vector.h> > #include <wtf/text/WTFString.h> > >@@ -1724,6 +1725,10 @@ private: > void contentFilterDidBlockLoadForFrame(const WebCore::ContentFilterUnblockHandler&, uint64_t frameID); > #endif > >+ void tryReloadAfterProcessTermination(); >+ void resetRecentCrashCountSoon(); >+ void resetRecentCrashCount(); >+ > API::DiagnosticLoggingClient* effectiveDiagnosticLoggingClient(WebCore::ShouldSample); > > void dispatchActivityStateChange(); >@@ -2173,6 +2178,10 @@ private: > // and have a global collection of them per process pool > // (e.g. for that process pool's page cache) > RefPtr<SuspendedPageProxy> m_suspendedPage; >+ >+ RunLoop::Timer<WebPageProxy> m_resetRecentCrashCountTimer; >+ unsigned m_recentCrashCount { 0 }; >+ > }; > > } // namespace WebKit >diff --git a/Tools/ChangeLog b/Tools/ChangeLog >index 8b15b317e4cf1e782c38165d3dd722929a1ca8d8..40e3efc9d786a13288f0f506d8554223858ac63a 100644 >--- a/Tools/ChangeLog >+++ b/Tools/ChangeLog >@@ -1,3 +1,17 @@ >+2018-06-10 Chris Dumez <cdumez@apple.com> >+ >+ Reload the Web view in case of crash if the client does not implement webViewWebContentProcessDidTerminate delegate >+ https://bugs.webkit.org/show_bug.cgi?id=186468 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Add API test coverage. >+ >+ * TestWebKitAPI/Tests/WebKitCocoa/WebContentProcessDidTerminate.mm: >+ (-[BasicNavigationDelegateWithoutCrashHandler webView:didStartProvisionalNavigation:]): >+ (-[BasicNavigationDelegateWithoutCrashHandler webView:didFinishNavigation:]): >+ (TEST): >+ > 2018-06-08 Darin Adler <darin@apple.com> > > [Cocoa] Remove all uses of NSAutoreleasePool as part of preparation for ARC >diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebContentProcessDidTerminate.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebContentProcessDidTerminate.mm >index 82e82cf4bd98750d1b0eda24a06c17ce7f04dd86..80526d1213c3267dccbf706208e5d02b6b401c6b 100644 >--- a/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebContentProcessDidTerminate.mm >+++ b/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebContentProcessDidTerminate.mm >@@ -33,10 +33,12 @@ > #import <WebKit/WKNavigationPrivate.h> > #import <WebKit/WKProcessPoolPrivate.h> > #import <WebKit/WKWebView.h> >+#import <WebKit/WKWebViewPrivate.h> > #import <wtf/RetainPtr.h> > > static bool didCrash; > static _WKProcessTerminationReason expectedCrashReason; >+static bool startedLoad; > static bool finishedLoad; > static bool shouldLoadAgainOnCrash; > static bool receivedScriptMessage; >@@ -67,6 +69,22 @@ - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigat > > @end > >+@interface BasicNavigationDelegateWithoutCrashHandler : NSObject <WKNavigationDelegate> >+@end >+ >+@implementation BasicNavigationDelegateWithoutCrashHandler >+ >+- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation >+{ >+ startedLoad = true; >+} >+ >+- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation >+{ >+ finishedLoad = true; >+} >+@end >+ > @interface CrashRecoveryScriptMessageHandler : NSObject <WKScriptMessageHandler> > @end > >@@ -154,4 +172,38 @@ TEST(WKNavigation, FailureToStartWebProcessAfterCrashRecovery) > EXPECT_TRUE(receivedScriptMessage); > } > >+TEST(WKNavigation, AutomaticViewReloadAfterWebProcessCrash) >+{ >+ RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]); >+ >+ auto delegate = adoptNS([[BasicNavigationDelegateWithoutCrashHandler alloc] init]); >+ [webView setNavigationDelegate:delegate.get()]; >+ >+ startedLoad = false; >+ finishedLoad = false; >+ >+ [webView loadRequest:[NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"rich-and-plain-text" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]]]; >+ TestWebKitAPI::Util::run(&finishedLoad); >+ >+ startedLoad = false; >+ finishedLoad = false; >+ >+ // Simulate crash. >+ [webView _killWebContentProcess]; >+ >+ // Since we do not deal with the crash, WebKit should attempt a reload. >+ TestWebKitAPI::Util::run(&finishedLoad); >+ >+ startedLoad = false; >+ finishedLoad = false; >+ >+ // Simulate another crash. >+ [webView _killWebContentProcess]; >+ >+ // WebKit should not attempt to reload again. >+ EXPECT_FALSE(startedLoad); >+ TestWebKitAPI::Util::sleep(0.5); >+ EXPECT_FALSE(startedLoad); >+} >+ > #endif // WK_API_ENABLED
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 186468
:
342376
| 342390