chromium/ios/web/js_features/window_error/window_error_java_script_feature.mm

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/web/js_features/window_error/window_error_java_script_feature.h"

#import "base/debug/crash_logging.h"
#import "base/debug/dump_without_crashing.h"
#import "base/feature_list.h"
#import "base/metrics/histogram_macros.h"
#import "base/strings/sys_string_conversions.h"
#import "ios/web/common/features.h"
#import "ios/web/public/js_messaging/java_script_feature_util.h"
#import "ios/web/public/js_messaging/script_message.h"
#import "net/base/apple/url_conversions.h"

namespace {
const char kScriptName[] = "error";

const char kWindowErrorResultHandlerName[] = "WindowErrorResultHandler";

static const char kScriptMessageResponseFilenameKey[] = "filename";
static const char kScriptMessageResponseLineNumberKey[] = "line_number";
static const char kScriptMessageResponseMessageKey[] = "message";
}  // namespace

namespace web {

WindowErrorJavaScriptFeature::ErrorDetails::ErrorDetails()
    : is_main_frame(true) {}
WindowErrorJavaScriptFeature::ErrorDetails::~ErrorDetails() = default;

WindowErrorJavaScriptFeature::WindowErrorJavaScriptFeature(
    base::RepeatingCallback<void(ErrorDetails)> callback)
    : JavaScriptFeature(
          ContentWorld::kIsolatedWorld,
          {FeatureScript::CreateWithFilename(
              kScriptName,
              FeatureScript::InjectionTime::kDocumentStart,
              FeatureScript::TargetFrames::kAllFrames,
              FeatureScript::ReinjectionBehavior::
                  kReinjectOnDocumentRecreation)},
          {web::java_script_features::GetCommonJavaScriptFeature()}),
      callback_(std::move(callback)) {
  DCHECK(callback_);
}
WindowErrorJavaScriptFeature::~WindowErrorJavaScriptFeature() = default;

std::optional<std::string>
WindowErrorJavaScriptFeature::GetScriptMessageHandlerName() const {
  return kWindowErrorResultHandlerName;
}

void WindowErrorJavaScriptFeature::ScriptMessageReceived(
    WebState* web_state,
    const ScriptMessage& script_message) {
  ErrorDetails details;

  const base::Value::Dict* script_dict =
      script_message.body() ? script_message.body()->GetIfDict() : nullptr;
  if (!script_dict) {
    return;
  }

  const std::string* filename =
      script_dict->FindString(kScriptMessageResponseFilenameKey);
  if (filename) {
    details.filename = base::SysUTF8ToNSString(*filename);
  }

  auto line_number =
      script_dict->FindDouble(kScriptMessageResponseLineNumberKey);
  if (line_number) {
    details.line_number = static_cast<int>(line_number.value());
  }

  const std::string* log_message =
      script_dict->FindString(kScriptMessageResponseMessageKey);
  if (log_message) {
    details.message = base::SysUTF8ToNSString(*log_message);
  }

  details.is_main_frame = script_message.is_main_frame();

  if (script_message.request_url()) {
    details.url = script_message.request_url().value();
  }

  bool has_scriptname = details.filename && details.filename.length > 0;
  UMA_HISTOGRAM_BOOLEAN("IOS.Javascript.ErrorHasFilename", has_scriptname);

  if (base::FeatureList::IsEnabled(features::kLogJavaScriptErrors) &&
      !has_scriptname) {
    if (log_message) {
      SCOPED_CRASH_KEY_STRING256("Javascript", "error", *log_message);
    }
    base::debug::DumpWithoutCrashing();
  }

  callback_.Run(details);
}

}  // namespace web