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
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236

android_webview / js_sandbox / service / js_sandbox_isolate.h [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.

#ifndef ANDROID_WEBVIEW_JS_SANDBOX_SERVICE_JS_SANDBOX_ISOLATE_H_
#define ANDROID_WEBVIEW_JS_SANDBOX_SERVICE_JS_SANDBOX_ISOLATE_H_

#include <memory>
#include <set>
#include <string>
#include <unordered_map>

#include "base/android/scoped_java_ref.h"
#include "base/compiler_specific.h"
#include "base/files/scoped_file.h"
#include "base/functional/callback_forward.h"
#include "base/memory/scoped_refptr.h"
#include "base/synchronization/lock.h"
#include "base/thread_annotations.h"
#include "v8/include/v8-array-buffer.h"
#include "v8/include/v8-inspector.h"
#include "v8/include/v8-promise.h"

namespace base {
class CancelableTaskTracker;
class SequencedTaskRunner;
class SingleThreadTaskRunner;
}  // namespace base

namespace gin {
class Arguments;
class IsolateHolder;
class ContextHolder;
}  // namespace gin

namespace v8 {
class ObjectTemplate;
}  // namespace v8

namespace android_webview {

class FdWithLength;
class JsSandboxArrayBufferAllocator;
class JsSandboxIsolateCallback;

class JsSandboxIsolate {
 public:
  explicit JsSandboxIsolate(
      const base::android::JavaParamRef<jobject>& j_isolate_,
      size_t max_heap_size_bytes);
  ~JsSandboxIsolate();

  jboolean EvaluateJavascript(
      JNIEnv* env,
      const base::android::JavaParamRef<jobject>& obj,
      const base::android::JavaParamRef<jstring>& jcode,
      const base::android::JavaParamRef<jobject>& j_callback);
  jboolean EvaluateJavascriptWithFd(
      JNIEnv* env,
      const base::android::JavaParamRef<jobject>& obj,
      const jint fd,
      const jlong length,
      const jlong offset,
      const base::android::JavaParamRef<jobject>& j_callback,
      const base::android::JavaParamRef<jobject>& pfd);
  void DestroyNative(JNIEnv* env,
                     const base::android::JavaParamRef<jobject>& obj);
  jboolean ProvideNamedData(JNIEnv* env,
                            const base::android::JavaParamRef<jobject>& obj,
                            const base::android::JavaParamRef<jstring>& jname,
                            const jint fd,
                            const jint length);
  // May enable or disable inspection, as needed.
  void SetConsoleEnabled(JNIEnv* env,
                         const base::android::JavaParamRef<jobject>& obj,
                         jboolean enable);

 private:
  class InspectorClient;

  void DeleteSelf();
  void InitializeIsolateOnThread();
  // Will enabled or disable inspection depending on whether any dynamic
  // features require it (for example, console logging).
  void EvaluateJavascriptOnThread(
      std::string code,
      scoped_refptr<JsSandboxIsolateCallback> callback);
  void PromiseFulfillCallback(scoped_refptr<JsSandboxIsolateCallback> callback,
                              gin::Arguments* args);
  void PromiseRejectCallback(scoped_refptr<JsSandboxIsolateCallback> callback,
                             gin::Arguments* args);

  void TerminateAndDestroy();
  void DestroyWhenPossible();
  void NotifyInitComplete();
  void CreateCancelableTaskTracker();
  void PostEvaluationToIsolateThread(
      std::string code,
      scoped_refptr<JsSandboxIsolateCallback> callback);
  void PostFileDescriptorReadToIsolateThread(
      int fd,
      int64_t length,
      int64_t offset,
      base::android::ScopedJavaGlobalRef<jobject> pfd,
      scoped_refptr<JsSandboxIsolateCallback> callback);
  void ReadFileDescriptorOnThread(
      int fd,
      int64_t length,
      int64_t offset,
      base::android::ScopedJavaGlobalRef<jobject> pfd,
      scoped_refptr<JsSandboxIsolateCallback> callback);
  void ReportFileDescriptorIOError(
      base::android::ScopedJavaGlobalRef<jobject> pfd,
      scoped_refptr<JsSandboxIsolateCallback> callback,
      std::string errorMessage);
  void ConvertPromiseToArrayBufferInThreadPool(
      base::ScopedFD fd,
      ssize_t length,
      std::string name,
      std::unique_ptr<v8::Global<v8::ArrayBuffer>> array_buffer,
      std::unique_ptr<v8::Global<v8::Promise::Resolver>> resolver,
      void* inner_buffer);
  void ConvertPromiseToArrayBufferInControlSequence(
      std::string name,
      std::unique_ptr<v8::Global<v8::ArrayBuffer>> array_buffer,
      std::unique_ptr<v8::Global<v8::Promise::Resolver>> resolver);
  void ConvertPromiseToFailureInControlSequence(
      std::string name,
      std::unique_ptr<v8::Global<v8::ArrayBuffer>> array_buffer,
      std::unique_ptr<v8::Global<v8::Promise::Resolver>> resolver,
      std::string reason);
  void ConvertPromiseToFailureInIsolateSequence(
      std::string name,
      std::unique_ptr<v8::Global<v8::ArrayBuffer>> array_buffer,
      std::unique_ptr<v8::Global<v8::Promise::Resolver>> resolver,
      std::string reason);
  void ConvertPromiseToArrayBufferInIsolateSequence(
      std::string name,
      std::unique_ptr<v8::Global<v8::ArrayBuffer>> array_buffer,
      std::unique_ptr<v8::Global<v8::Promise::Resolver>> resolver);

  void ConsumeNamedDataAsArrayBuffer(gin::Arguments* args);
  v8::Local<v8::ObjectTemplate> CreateAndroidNamespaceTemplate(
      v8::Isolate* isolate);

  // Must only be used from isolate thread
  [[noreturn]] static size_t NearHeapLimitCallback(void* data,
                                                   size_t current_heap_limit,
                                                   size_t initial_heap_limit);
  v8::MaybeLocal<v8::ArrayBuffer> tryAllocateArrayBuffer(size_t length);

  // Must only be used from isolate thread
  [[noreturn]] void MemoryLimitExceeded();
  // Must only be used from isolate thread
  void ReportOutOfMemory();
  [[noreturn]] void FreezeThread();

  void EnableOrDisableInspectorAsNeeded();
  void SetConsoleEnabledOnControlThread(bool enable);
  void SetConsoleEnabledOnIsolateThread(bool enable);

  // Remove a callback from the ongoing_evaluation_callbacks_ set.
  //
  // Returns the passed reference to allow chaining. (The caller must make sure
  // the reference continues to be valid if it is used.)
  //
  // Must only be used from isolate thread.
  const scoped_refptr<JsSandboxIsolateCallback>& UseCallback(
      const scoped_refptr<JsSandboxIsolateCallback>& callback);

  // Java-side JsSandboxIsolate object corresponding to this isolate.
  const base::android::ScopedJavaGlobalRef<jobject> j_isolate_;

  // V8 heap size limit. Must be non-negative.
  //
  // 0 indicates no explicit limit (but use the default V8 limits).
  const size_t isolate_max_heap_size_bytes_;
  // Apart from construction/destruction, must only be used from the isolate
  // thread.
  std::unique_ptr<JsSandboxArrayBufferAllocator> array_buffer_allocator_;

  // Used as a control sequence to add ordering to binder threadpool requests.
  scoped_refptr<base::SequencedTaskRunner> control_task_runner_;
  // Should be used from control_task_runner_.
  bool isolate_init_complete = false;
  // Should be used from control_task_runner_.
  bool destroy_called_before_init = false;
  // Should be used from control_task_runner_.
  std::unique_ptr<base::CancelableTaskTracker> cancelable_task_tracker_;

  // Used for interaction with the isolate.
  scoped_refptr<base::SingleThreadTaskRunner> isolate_task_runner_;
  // Should be used from isolate_task_runner_.
  std::unique_ptr<gin::IsolateHolder> isolate_holder_;
  // Should be used from isolate_task_runner_.
  //
  // This isolate scope is entered during initialization from inside the
  // isolate_task_runner_ thread and exited only at isolate teardown. It is thus
  // used implicitly by all tasks run on the isolate thread.
  std::unique_ptr<v8::Isolate::Scope> isolate_scope_;
  // Should be used from isolate_task_runner_.
  std::unique_ptr<gin::ContextHolder> context_holder_;

  base::Lock named_fd_lock_;
  std::unordered_map<std::string, FdWithLength> named_fd_
      GUARDED_BY(named_fd_lock_);

  // Callbacks associated with evaluations which have begun but not yet
  // finished. More precisely, these are callbacks which have been passed to
  // EvaluateJavascriptOnThread, but which have not yet been called with a final
  // result or error.
  //
  // When a callback is used to send a result or error, it should be removed via
  // UseCallback().
  //
  // This may contain multiple items when evaluations return a promise that must
  // be resolved asynchronously. However, an empty set does not imply that the
  // isolate is idle, as this does not track microtasks or background processing
  // which may result in further execution, such as WASM compilation promises.
  //
  // Used for signaling errors from V8 callbacks.
  std::set<scoped_refptr<JsSandboxIsolateCallback>>
      ongoing_evaluation_callbacks_;

  bool console_enabled_;

  // Inspector objects should be destructed before anything they're inspecting,
  // so they are later in the field list.
  std::unique_ptr<v8_inspector::V8InspectorClient> inspector_client_;
  std::unique_ptr<v8_inspector::V8Inspector> inspector_;
  std::unique_ptr<v8_inspector::V8Inspector::Channel> inspector_channel_;
  std::unique_ptr<v8_inspector::V8InspectorSession> inspector_session_;
};
}  // namespace android_webview

#endif  // ANDROID_WEBVIEW_JS_SANDBOX_SERVICE_JS_SANDBOX_ISOLATE_H_