diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn index 89ead5d469c7e..7319ad3ac1293 100644 --- a/android_webview/BUILD.gn +++ b/android_webview/BUILD.gn @@ -350,6 +350,7 @@ source_set("common") { "browser/aw_quota_manager_bridge.h", "browser/aw_quota_permission_context.cc", "browser/aw_quota_permission_context.h", + "browser/aw_render_process_gone_delegate.h", "browser/aw_render_thread_context_provider.cc", "browser/aw_render_thread_context_provider.h", "browser/aw_resource_context.cc", diff --git a/android_webview/browser/aw_browser_terminator.cc b/android_webview/browser/aw_browser_terminator.cc index 23c42856fdf84..34052e0a09940 100644 --- a/android_webview/browser/aw_browser_terminator.cc +++ b/android_webview/browser/aw_browser_terminator.cc @@ -6,6 +6,7 @@ #include +#include "android_webview/browser/aw_render_process_gone_delegate.h" #include "android_webview/common/aw_descriptors.h" #include "android_webview/common/crash_reporter/aw_microdump_crash_reporter.h" #include "base/bind.h" @@ -17,11 +18,64 @@ #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" #include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/render_widget_host.h" +#include "content/public/browser/render_widget_host_iterator.h" +#include "content/public/browser/web_contents.h" using content::BrowserThread; namespace android_webview { +namespace { + +void GetAwRenderProcessGoneDelegatesForRenderProcess( + int render_process_id, + std::vector* delegates) { + content::RenderProcessHost* rph = + content::RenderProcessHost::FromID(render_process_id); + if (!rph) + return; + + std::unique_ptr widgets( + content::RenderWidgetHost::GetRenderWidgetHosts()); + while (content::RenderWidgetHost* widget = widgets->GetNextHost()) { + content::RenderViewHost* view = content::RenderViewHost::From(widget); + if (view && rph == view->GetProcess()) { + content::WebContents* wc = content::WebContents::FromRenderViewHost(view); + if (wc) { + AwRenderProcessGoneDelegate* delegate = + AwRenderProcessGoneDelegate::FromWebContents(wc); + if (delegate) + delegates->push_back(delegate); + } + } + } +} + +void OnRenderProcessGone(int child_process_id) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + std::vector delegates; + GetAwRenderProcessGoneDelegatesForRenderProcess(child_process_id, &delegates); + for (auto delegate : delegates) + delegate->OnRenderProcessGone(child_process_id); +} + +void OnRenderProcessGoneDetail(int child_process_id, bool crashed) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + std::vector delegates; + GetAwRenderProcessGoneDelegatesForRenderProcess(child_process_id, &delegates); + for (auto delegate : delegates) { + if (!delegate->OnRenderProcessGoneDetail(child_process_id, crashed)) { + // Keeps this log unchanged, CTS test uses it to detect crash. + LOG(FATAL) << "Render process's abnormal termination wasn't handled by" + << " all associated webviews, triggering application crash"; + } + } +} + +} // namespace + AwBrowserTerminator::AwBrowserTerminator() {} AwBrowserTerminator::~AwBrowserTerminator() {} @@ -43,19 +97,23 @@ void AwBrowserTerminator::OnChildStart(int child_process_id, } void AwBrowserTerminator::ProcessTerminationStatus( + int child_process_id, std::unique_ptr pipe) { + bool crashed = false; + + // If the child process hasn't written anything into the pipe. This implies + // that it was terminated via SIGKILL by the low memory killer. if (pipe->Peek() >= sizeof(int)) { int exit_code; pipe->Receive(&exit_code, sizeof(exit_code)); crash_reporter::SuppressDumpGeneration(); - LOG(FATAL) << "Renderer process crash detected (code " << exit_code - << "). Terminating browser."; - } else { - // The child process hasn't written anything into the pipe. This implies - // that it was terminated via SIGKILL by the low memory killer, and thus we - // need to perform a clean exit. - exit(0); + LOG(ERROR) << "Renderer process crash detected (code " << exit_code << ")."; + crashed = true; } + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&OnRenderProcessGoneDetail, child_process_id, crashed)); } void AwBrowserTerminator::OnChildExit( @@ -79,10 +137,12 @@ void AwBrowserTerminator::OnChildExit( } if (termination_status == base::TERMINATION_STATUS_NORMAL_TERMINATION) return; + OnRenderProcessGone(child_process_id); DCHECK(pipe->handle() != base::SyncSocket::kInvalidHandle); BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, base::Bind(&AwBrowserTerminator::ProcessTerminationStatus, + child_process_id, base::Passed(std::move(pipe)))); } diff --git a/android_webview/browser/aw_browser_terminator.h b/android_webview/browser/aw_browser_terminator.h index 9c585a6ec4803..6df7c38b03286 100644 --- a/android_webview/browser/aw_browser_terminator.h +++ b/android_webview/browser/aw_browser_terminator.h @@ -39,7 +39,8 @@ class AwBrowserTerminator : public breakpad::CrashDumpObserver::Client { base::android::ApplicationState app_state) override; private: - static void ProcessTerminationStatus(std::unique_ptr pipe); + static void ProcessTerminationStatus(int child_process_id, + std::unique_ptr pipe); // This map should only be accessed with its lock aquired as it is accessed // from the PROCESS_LAUNCHER, FILE, and UI threads. diff --git a/android_webview/browser/aw_render_process_gone_delegate.h b/android_webview/browser/aw_render_process_gone_delegate.h new file mode 100644 index 0000000000000..fb8661b33cf8e --- /dev/null +++ b/android_webview/browser/aw_render_process_gone_delegate.h @@ -0,0 +1,38 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ANDROID_WEBVIEW_BROWSER_AW_RENDER_PROCESS_GONE_DELEGATE_H_ +#define ANDROID_WEBVIEW_BROWSER_AW_RENDER_PROCESS_GONE_DELEGATE_H_ + +namespace content { +class WebContents; +} + +namespace android_webview { + +// Delegate interface to handle the events that render process was gone. +class AwRenderProcessGoneDelegate { + public: + // Returns the AwRenderProcessGoneDelegate instance associated with + // the given |web_contents|. + static AwRenderProcessGoneDelegate* FromWebContents( + content::WebContents* web_contents); + + // Notify render process's termination is detected. + virtual void OnRenderProcessGone(int child_process_id) = 0; + + // Notify if render process crashed or was killed, this callback is + // invoked after OnRenderProcessGone() because the detail information + // can't be obtained immediately when render process's termination is + // detected. + virtual bool OnRenderProcessGoneDetail(int child_process_id, + bool crashed) = 0; + + protected: + AwRenderProcessGoneDelegate() {} +}; + +} // namespace android_webview + +#endif // ANDROID_WEBVIEW_BROWSER_AW_RENDER_PROCESS_GONE_DELEGATE_H_ diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java index 3c789da95b4b0..6e40ae76fc1aa 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContents.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java @@ -1157,6 +1157,16 @@ private void receivePopupContents(long popupNativeAwContents) { } } + @CalledByNative + private void onRenderProcessGone(int childProcessID) { + } + + @CalledByNative + private boolean onRenderProcessGoneDetail(int childProcessID, boolean crashed) { + if (isDestroyed(NO_WARN)) return false; + return mContentsClient.onRenderProcessGone(new AwRenderProcessGoneDetail(crashed)); + } + /** * Destroys this object and deletes its native counterpart. */ diff --git a/android_webview/native/aw_contents.cc b/android_webview/native/aw_contents.cc index bfe2e967317d1..dfd03265d3a4b 100644 --- a/android_webview/native/aw_contents.cc +++ b/android_webview/native/aw_contents.cc @@ -186,6 +186,12 @@ AwSafeBrowsingUIManager::UIManagerClient::FromWebContents( return AwContents::FromWebContents(web_contents); } +// static +AwRenderProcessGoneDelegate* AwRenderProcessGoneDelegate::FromWebContents( + content::WebContents* web_contents) { + return AwContents::FromWebContents(web_contents); +} + AwContents::AwContents(std::unique_ptr web_contents) : content::WebContentsObserver(web_contents.get()), functor_(nullptr), @@ -1315,4 +1321,26 @@ bool AwContents::CanShowInterstitial() { return Java_AwContents_canShowInterstitial(env, obj); } +void AwContents::OnRenderProcessGone(int child_process_id) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef obj = java_ref_.get(env); + if (obj.is_null()) + return; + + Java_AwContents_onRenderProcessGone(env, obj, child_process_id); +} + +bool AwContents::OnRenderProcessGoneDetail(int child_process_id, + bool crashed) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef obj = java_ref_.get(env); + if (obj.is_null()) + return false; + + return Java_AwContents_onRenderProcessGoneDetail(env, obj, + child_process_id, crashed); +} + } // namespace android_webview diff --git a/android_webview/native/aw_contents.h b/android_webview/native/aw_contents.h index a51c450d3e9cb..761a723afc821 100644 --- a/android_webview/native/aw_contents.h +++ b/android_webview/native/aw_contents.h @@ -13,6 +13,7 @@ #include #include "android_webview/browser/aw_browser_permission_request_delegate.h" +#include "android_webview/browser/aw_render_process_gone_delegate.h" #include "android_webview/browser/aw_safe_browsing_ui_manager.h" #include "android_webview/browser/browser_view_renderer.h" #include "android_webview/browser/browser_view_renderer_client.h" @@ -64,6 +65,7 @@ class AwContents : public FindHelper::Listener, public BrowserViewRendererClient, public PermissionRequestHandlerClient, public AwBrowserPermissionRequestDelegate, + public AwRenderProcessGoneDelegate, public content::WebContentsObserver, public AwSafeBrowsingUIManager::UIManagerClient { public: @@ -344,6 +346,10 @@ class AwContents : public FindHelper::Listener, // AwSafeBrowsingUIManager::UIManagerClient implementation bool CanShowInterstitial() override; + // AwRenderProcessGoneDelegate overrides + void OnRenderProcessGone(int child_process_id) override; + bool OnRenderProcessGoneDetail(int child_process_id, bool crashed) override; + private: void InitAutofillIfNecessary(bool enabled);