#include "content/browser/webui/web_ui_main_frame_observer.h"
#include <string>
#include <utility>
#include "build/build_config.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/webui/web_ui_impl.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_ui_controller.h"
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#include "base/feature_list.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
#include "components/crash/content/browser/error_reporting/javascript_error_report.h"
#include "components/crash/content/browser/error_reporting/js_error_report_processor.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.h"
#include "content/public/common/url_constants.h"
#include "url/gurl.h"
#endif
namespace content {
namespace {
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
std::string RedactURL(const GURL& url) {
std::string redacted_url = url.DeprecatedGetOriginAsURL().spec();
if (!redacted_url.empty() && redacted_url.back() == '/') {
redacted_url.pop_back();
}
base::StrAppend(&redacted_url, {url.path_piece()});
return redacted_url;
}
#endif
}
WebUIMainFrameObserver::WebUIMainFrameObserver(WebUIImpl* web_ui,
WebContents* contents)
: WebContentsObserver(contents), web_ui_(web_ui) {}
WebUIMainFrameObserver::~WebUIMainFrameObserver() = default;
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
void WebUIMainFrameObserver::OnDidAddMessageToConsole(
RenderFrameHost* source_frame,
blink::mojom::ConsoleMessageLevel log_level,
const std::u16string& message,
int32_t line_no,
const std::u16string& source_id,
const absl::optional<std::u16string>& untrusted_stack_trace) {
DVLOG(3) << "OnDidAddMessageToConsole called for " << message;
if (untrusted_stack_trace) {
DVLOG(3) << "stack is " << *untrusted_stack_trace;
}
if (!error_reporting_enabled_) {
DVLOG(3) << "Message not reported, error reporting disabled for this page "
"or experiment is off";
return;
}
if (log_level != blink::mojom::ConsoleMessageLevel::kError) {
DVLOG(3) << "Message not reported, not an error";
return;
}
if (source_frame != web_ui_->GetRenderFrameHost()) {
DVLOG(3) << "Message not reported, different frame";
return;
}
scoped_refptr<JsErrorReportProcessor> processor =
JsErrorReportProcessor::Get();
if (!processor) {
DVLOG(3) << "Message not reported, no processor";
return;
}
GURL url(source_id);
if (!url.is_valid()) {
DVLOG(3) << "Message not reported, invalid URL";
return;
}
if (!url.SchemeIs(kChromeUIScheme)) {
DVLOG(3) << "Message not reported, not a chrome:// URL";
return;
}
JavaScriptErrorReport report;
report.message = base::UTF16ToUTF8(message);
report.line_number = line_no;
report.url = RedactURL(url);
report.source_system = JavaScriptErrorReport::SourceSystem::kWebUIObserver;
if (untrusted_stack_trace) {
report.stack_trace = base::UTF16ToUTF8(*untrusted_stack_trace);
}
GURL page_url = source_frame->GetLastCommittedURL();
if (page_url.is_valid()) {
report.page_url = RedactURL(page_url);
}
DVLOG(3) << "Error being sent to Google";
processor->SendErrorReport(std::move(report), base::DoNothing(),
web_contents()->GetBrowserContext());
}
void WebUIMainFrameObserver::MaybeEnableWebUIJavaScriptErrorReporting(
NavigationHandle* navigation_handle) {
error_reporting_enabled_ =
web_ui_->GetController()->IsJavascriptErrorReportingEnabled();
if (error_reporting_enabled_) {
DVLOG(3) << "Enabled";
static_cast<content::RenderFrameHostImpl*>(web_ui_->GetRenderFrameHost())
->SetWantErrorMessageStackTrace();
} else {
DVLOG(3) << "Error reporting disabled for this page";
}
}
#endif
void WebUIMainFrameObserver::ReadyToCommitNavigation(
NavigationHandle* navigation_handle) {
if (navigation_handle->GetRenderFrameHost() !=
web_ui_->GetRenderFrameHost()) {
return;
}
web_ui_->GetController()->WebUIReadyToCommitNavigation(
web_ui_->GetRenderFrameHost());
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
MaybeEnableWebUIJavaScriptErrorReporting(navigation_handle);
#endif
}
void WebUIMainFrameObserver::PrimaryPageChanged(Page& page) {
web_ui_->DisallowJavascriptOnAllHandlers();
web_ui_->GetController()->WebUIPrimaryPageChanged(page);
}
}