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

android_webview / js_sandbox / service / js_sandbox_isolate_callback.cc [blame]

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

#include "android_webview/js_sandbox/service/js_sandbox_isolate_callback.h"

#include <jni.h>
#include <sstream>

#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/files/file_util.h"

// Must come after all headers that specialize FromJniType() / ToJniType().
#include "android_webview/js_sandbox/js_sandbox_jni_headers/JsSandboxIsolateCallback_jni.h"
#include "android_webview/js_sandbox/js_sandbox_jni_headers/JsSandboxIsolateFdCallback_jni.h"

namespace android_webview {

JsSandboxIsolateCallback::JsSandboxIsolateCallback(
    base::android::ScopedJavaGlobalRef<jobject>&& callback,
    bool use_fd)
    : callback_(std::move(callback)), use_fd(use_fd) {
  CHECK(callback_) << "JsSandboxIsolateCallback java object is null";
}

JsSandboxIsolateCallback::~JsSandboxIsolateCallback() = default;

void JsSandboxIsolateCallback::ReportResult(const std::string& result) {
  JNIEnv* env = base::android::AttachCurrentThread();
  if (use_fd) {
    base::ScopedFD read_fd, write_fd;
    CHECK(base::CreatePipe(&read_fd, &write_fd));
    Java_JsSandboxIsolateFdCallback_onResult(
        env, UseCallback(), static_cast<jint>(read_fd.release()),
        static_cast<jint>(result.length()));
    // This might return false due to EPIPE if the client closes the fd without
    // reading from it. That is not an error for our use case.
    base::WriteFileDescriptor(write_fd.get(), std::move(result));
  } else {
    base::android::ScopedJavaLocalRef<jstring> java_string_result =
        base::android::ConvertUTF8ToJavaString(env, result);
    Java_JsSandboxIsolateCallback_onResult(env, UseCallback(),
                                           java_string_result);
  }
}

void JsSandboxIsolateCallback::ReportJsEvaluationError(
    const std::string& error) {
  ReportError(ErrorType::kJsEvaluationError, error);
}

void JsSandboxIsolateCallback::ReportFileDescriptorIOFailedError(
    const std::string& error) {
  ReportError(ErrorType::kFileDescriptorIOFailedError, error);
}

void JsSandboxIsolateCallback::ReportMemoryLimitExceededError(
    const uint64_t memory_limit,
    const uint64_t v8_heap_usage,
    const uint64_t non_v8_heap_usage) {
  std::ostringstream details;
  details << "Memory limit exceeded.\n";
  if (memory_limit > 0) {
    details << "Memory limit: " << memory_limit << " bytes\n";
  } else {
    details << "Memory limit not explicitly configured\n";
  }
  details << "V8 heap usage: " << v8_heap_usage << " bytes\n";
  details << "Non-V8 heap usage: " << non_v8_heap_usage << " bytes\n";
  ReportError(ErrorType::kMemoryLimitExceeded, details.str());
}

void JsSandboxIsolateCallback::ReportError(const ErrorType error_type,
                                           const std::string& error) {
  JNIEnv* env = base::android::AttachCurrentThread();
  if (use_fd) {
    base::ScopedFD read_fd, write_fd;
    CHECK(base::CreatePipe(&read_fd, &write_fd));
    Java_JsSandboxIsolateFdCallback_onError(
        env, UseCallback(), static_cast<jint>(error_type),
        static_cast<jint>(read_fd.release()),
        static_cast<jint>(error.length()));
    // This might return false due to EPIPE if the client closes the fd without
    // reading from it. That is not an error for our use case.
    base::WriteFileDescriptor(write_fd.get(), std::move(error));
  } else {
    base::android::ScopedJavaLocalRef<jstring> java_string_error =
        base::android::ConvertUTF8ToJavaString(env, error);
    Java_JsSandboxIsolateCallback_onError(
        env, UseCallback(), static_cast<jint>(error_type), java_string_error);
  }
}

base::android::ScopedJavaGlobalRef<jobject>
JsSandboxIsolateCallback::UseCallback() {
  CHECK(callback_) << "Double use of isolate callback";
  // Move resets callback_ to null
  return std::move(callback_);
}

}  // namespace android_webview