1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/hung_renderer/hung_renderer_core.h"
#include <algorithm>
#include "base/i18n/rtl.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/tab_contents/core_tab_helper.h"
#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h"
#include "chrome/grit/generated_resources.h"
#include "components/url_formatter/url_formatter.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
namespace {
// Author's Note:
//
// The inefficiency of walking the frame tree repeatedly has not gone unnoticed.
// Given the different requirements of the Cocoa and Views implementations of
// the Hung Page dialog, this is the simplest way. When we consolidate
// implementations, it will be a good idea to reconsider this approach.
// Returns the first RenderFrameHost that has a process that matches
// `hung_process`.
content::RenderFrameHost* FindFirstRenderFrameHostMatchingProcess(
content::WebContents* web_contents,
content::RenderProcessHost* hung_process) {
content::RenderFrameHost* result = nullptr;
// We only consider frames visible to the user for hung frames. This is
// fine because only frames receiving input are considered hung.
web_contents->GetPrimaryMainFrame()->ForEachRenderFrameHostWithAction(
[hung_process, &result](content::RenderFrameHost* render_frame_host) {
if (render_frame_host->GetProcess() == hung_process) {
result = render_frame_host;
return content::RenderFrameHost::FrameIterationAction::kStop;
}
return content::RenderFrameHost::FrameIterationAction::kContinue;
});
return result;
}
// Returns whether the WebContents has a frame that is backed by the hung
// process.
bool IsWebContentsHung(content::WebContents* web_contents,
content::RenderProcessHost* hung_process) {
return FindFirstRenderFrameHostMatchingProcess(web_contents, hung_process) !=
nullptr;
}
// Returns the URL of the first hung frame encountered in the WebContents.
GURL GetURLOfAnyHungFrame(content::WebContents* web_contents,
content::RenderProcessHost* hung_process) {
content::RenderFrameHost* render_frame_host =
FindFirstRenderFrameHostMatchingProcess(web_contents, hung_process);
// If a frame is attempting to commit a navigation into a hung renderer
// process, then its |frame->GetProcess()| will still return the process
// hosting the previously committed navigation. In such case, the
// FindFirstRenderFrameHostMatchingProcess above might not find any matching
// frame.
if (!render_frame_host) {
return GURL();
}
return render_frame_host->GetLastCommittedURL();
}
} // namespace
std::vector<content::WebContents*> GetHungWebContentsList(
content::WebContents* hung_web_contents,
content::RenderProcessHost* hung_process) {
std::vector<content::WebContents*> result;
auto is_hung = [hung_process](content::WebContents* web_contents) {
return IsWebContentsHung(web_contents, hung_process) &&
!web_contents->IsCrashed();
};
std::ranges::copy_if(AllTabContentses(), std::back_inserter(result), is_hung);
// Move |hung_web_contents| to the front. It might be missing from the
// initial |results| when it hasn't yet committed a navigation into the hung
// process.
auto first = std::ranges::find(result, hung_web_contents);
if (first != result.end()) {
std::rotate(result.begin(), first, std::next(first));
} else {
result.insert(result.begin(), hung_web_contents);
}
return result;
}
// Given a WebContents that is affected by the hang, and the RenderProcessHost
// of the hung process, returns the title of the WebContents that should be used
// in the "Hung Page" dialog.
std::u16string GetHungWebContentsTitle(
content::WebContents* affected_web_contents,
content::RenderProcessHost* hung_process) {
std::u16string page_title = affected_web_contents->GetTitle();
if (page_title.empty()) {
page_title = CoreTabHelper::GetDefaultTitle();
}
// TODO(xji): Consider adding a special case if the title text is a URL,
// since those should always have LTR directionality. Please refer to
// http://crbug.com/6726 for more information.
base::i18n::AdjustStringForLocaleDirection(&page_title);
if (affected_web_contents->GetPrimaryMainFrame()->GetProcess() ==
hung_process) {
return page_title;
}
GURL hung_url = GetURLOfAnyHungFrame(affected_web_contents, hung_process);
if (!hung_url.is_valid() || !hung_url.has_host()) {
return page_title;
}
// N.B. using just the host here is OK since this is a notification and the
// user doesn't need to make a security critical decision about the page in
// this dialog.
std::u16string host_string;
url_formatter::AppendFormattedHost(hung_url, &host_string);
return l10n_util::GetStringFUTF16(IDS_BROWSER_HANGMONITOR_IFRAME_TITLE,
host_string, page_title);
}
|