chromium/chrome/browser/extensions/api/crash_report_private/crash_report_private_api.cc

// Copyright 2019 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/extensions/api/crash_report_private/crash_report_private_api.h"

#include "base/time/time.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/web_applications/app_browser_controller.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/devtools_agent_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"

namespace extensions {
namespace api {

namespace {

WindowType GetWindowType(content::WebContents* web_contents) {
  Browser* browser = chrome::FindBrowserWithTab(web_contents);
  if (!browser)
    return WindowType::kNoBrowser;
  if (!browser->app_controller())
    return WindowType::kRegularTabbed;
  if (browser->app_controller()->system_app())
    return WindowType::kSystemWebApp;
  return WindowType::kWebApp;
}

}  // namespace

CrashReportPrivateReportErrorFunction::CrashReportPrivateReportErrorFunction() =
    default;

CrashReportPrivateReportErrorFunction::
    ~CrashReportPrivateReportErrorFunction() = default;

ExtensionFunction::ResponseAction CrashReportPrivateReportErrorFunction::Run() {
  content::WebContents* web_contents = GetSenderWebContents();
  // Silently drop the crash report if devtools has ever been opened for this
  // |web_contents|.
  if (web_contents && content::DevToolsAgentHost::HasFor(web_contents)) {
    return RespondNow(NoArguments());
  }

  const auto params = crash_report_private::ReportError::Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(params);

  auto processor = JsErrorReportProcessor::Get();
  if (!processor) {
    VLOG(3) << "No processor for error report";
    return RespondNow(Error("No processor for error report"));
  }

  JavaScriptErrorReport error_report;
  error_report.message = std::move(params->info.message);
  error_report.url = std::move(params->info.url);
  error_report.source_system =
      JavaScriptErrorReport::SourceSystem::kCrashReportApi;
  if (params->info.product) {
    error_report.product = std::move(*params->info.product);
  }

  if (params->info.version) {
    error_report.version = std::move(*params->info.version);
  }

  if (params->info.line_number) {
    error_report.line_number = *params->info.line_number;
  }

  if (params->info.column_number) {
    error_report.column_number = *params->info.column_number;
  }

  if (params->info.debug_id) {
    error_report.debug_id = *params->info.debug_id;
  }

  if (params->info.stack_trace) {
    error_report.stack_trace = std::move(*params->info.stack_trace);
  }

  if (web_contents) {
    error_report.window_type = GetWindowType(web_contents);

    base::TimeTicks render_process_start_time =
        web_contents->GetPrimaryMainFrame()->GetProcess()->GetLastInitTime();
    base::TimeDelta render_process_uptime;
    if (!render_process_start_time.is_null()) {
      render_process_uptime =
          base::TimeTicks::Now() - render_process_start_time;
    }
    // Note: This can be 0 in tests or if the process isn't live (implying
    // process fails to start up or terminated). Report this anyways as it can
    // hint at race conditions.
    error_report.renderer_process_uptime_ms =
        render_process_uptime.InMilliseconds();
  }

  error_report.app_locale = g_browser_process->GetApplicationLocale();

  processor->SendErrorReport(
      std::move(error_report),
      base::BindOnce(&CrashReportPrivateReportErrorFunction::OnReportComplete,
                     this),
      browser_context());

  return RespondLater();
}

void CrashReportPrivateReportErrorFunction::OnReportComplete() {
  Respond(NoArguments());
}

}  // namespace api
}  // namespace extensions