Upgrade V8 to 8.8.278.14

Bug: 162604069
Bug: 167389063
Test: gts-tradefed run gts-dev --module GtsGmscoreHostTestCases
      --test com.google.android.gts.devicepolicy.DeviceOwnerTest#testProxyPacProxyTest
Test: m -j proxy_resolver_v8_unittest && adb sync && adb shell \
      /data/nativetest/proxy_resolver_v8_unittest/proxy_resolver_v8_unittest

Merged-In: Ifb09923b9d7f6d8990fb062d7dc0294edf2c098e
Change-Id: Ifb09923b9d7f6d8990fb062d7dc0294edf2c098e
(cherry picked from commit 9580a23bc5b8874a0979001d3595d027cbb68128)
diff --git a/src/inspector/v8-debugger-agent-impl.cc b/src/inspector/v8-debugger-agent-impl.cc
index 7de46a1..f82ce98 100644
--- a/src/inspector/v8-debugger-agent-impl.cc
+++ b/src/inspector/v8-debugger-agent-impl.cc
@@ -6,13 +6,15 @@
 
 #include <algorithm>
 
+#include "../../third_party/inspector_protocol/crdtp/json.h"
+#include "include/v8-inspector.h"
+#include "src/base/safe_conversions.h"
 #include "src/debug/debug-interface.h"
 #include "src/inspector/injected-script.h"
 #include "src/inspector/inspected-context.h"
-#include "src/inspector/java-script-call-frame.h"
+#include "src/inspector/protocol/Debugger.h"
 #include "src/inspector/protocol/Protocol.h"
 #include "src/inspector/remote-object-id.h"
-#include "src/inspector/script-breakpoint.h"
 #include "src/inspector/search-util.h"
 #include "src/inspector/string-util.h"
 #include "src/inspector/v8-debugger-script.h"
@@ -22,9 +24,7 @@
 #include "src/inspector/v8-regex.h"
 #include "src/inspector/v8-runtime-agent-impl.h"
 #include "src/inspector/v8-stack-trace-impl.h"
-#include "src/inspector/v8-value-copier.h"
-
-#include "include/v8-inspector.h"
+#include "src/inspector/v8-value-utils.h"
 
 namespace v8_inspector {
 
@@ -32,26 +32,27 @@
 using protocol::Maybe;
 using protocol::Debugger::BreakpointId;
 using protocol::Debugger::CallFrame;
+using protocol::Debugger::Scope;
 using protocol::Runtime::ExceptionDetails;
-using protocol::Runtime::ScriptId;
-using protocol::Runtime::StackTrace;
 using protocol::Runtime::RemoteObject;
+using protocol::Runtime::ScriptId;
+
+namespace InstrumentationEnum =
+    protocol::Debugger::SetInstrumentationBreakpoint::InstrumentationEnum;
 
 namespace DebuggerAgentState {
-static const char javaScriptBreakpoints[] = "javaScriptBreakopints";
 static const char pauseOnExceptionsState[] = "pauseOnExceptionsState";
 static const char asyncCallStackDepth[] = "asyncCallStackDepth";
 static const char blackboxPattern[] = "blackboxPattern";
 static const char debuggerEnabled[] = "debuggerEnabled";
-
-// Breakpoint properties.
-static const char url[] = "url";
-static const char isRegex[] = "isRegex";
-static const char lineNumber[] = "lineNumber";
-static const char columnNumber[] = "columnNumber";
-static const char condition[] = "condition";
 static const char skipAllPauses[] = "skipAllPauses";
 
+static const char breakpointsByRegex[] = "breakpointsByRegex";
+static const char breakpointsByUrl[] = "breakpointsByUrl";
+static const char breakpointsByScriptHash[] = "breakpointsByScriptHash";
+static const char breakpointHints[] = "breakpointHints";
+static const char instrumentationBreakpoints[] = "instrumentationBreakpoints";
+
 }  // namespace DebuggerAgentState
 
 static const char kBacktraceObjectGroup[] = "backtrace";
@@ -59,66 +60,284 @@
 static const char kDebuggerNotPaused[] =
     "Can only perform operation while paused.";
 
+static const size_t kBreakpointHintMaxLength = 128;
+static const intptr_t kBreakpointHintMaxSearchOffset = 80 * 10;
+// Limit the number of breakpoints returned, as we otherwise may exceed
+// the maximum length of a message in mojo (see https://crbug.com/1105172).
+static const size_t kMaxNumBreakpoints = 1000;
+
+// TODO(1099680): getScriptSource and getWasmBytecode return Wasm wire bytes
+// as protocol::Binary, which is encoded as JSON string in the communication
+// to the DevTools front-end and hence leads to either crashing the renderer
+// that is being debugged or the renderer that's running the front-end if we
+// allow arbitrarily big Wasm byte sequences here. Ideally we would find a
+// different way to transfer the wire bytes (middle- to long-term), but as a
+// short-term solution, we should at least not crash.
+static const size_t kWasmBytecodeMaxLength = (v8::String::kMaxLength / 4) * 3;
+static const char kWasmBytecodeExceedsTransferLimit[] =
+    "WebAssembly bytecode exceeds the transfer limit";
+
 namespace {
 
-void TranslateWasmStackTraceLocations(Array<CallFrame>* stackTrace,
-                                      WasmTranslation* wasmTranslation) {
-  for (size_t i = 0, e = stackTrace->length(); i != e; ++i) {
-    protocol::Debugger::Location* location = stackTrace->get(i)->getLocation();
-    String16 scriptId = location->getScriptId();
-    int lineNumber = location->getLineNumber();
-    int columnNumber = location->getColumnNumber(-1);
+enum class BreakpointType {
+  kByUrl = 1,
+  kByUrlRegex,
+  kByScriptHash,
+  kByScriptId,
+  kDebugCommand,
+  kMonitorCommand,
+  kBreakpointAtEntry,
+  kInstrumentationBreakpoint
+};
 
-    if (!wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
-            &scriptId, &lineNumber, &columnNumber)) {
-      continue;
-    }
-
-    location->setScriptId(std::move(scriptId));
-    location->setLineNumber(lineNumber);
-    location->setColumnNumber(columnNumber);
-  }
-}
-
-String16 breakpointIdSuffix(V8DebuggerAgentImpl::BreakpointSource source) {
-  switch (source) {
-    case V8DebuggerAgentImpl::UserBreakpointSource:
-      break;
-    case V8DebuggerAgentImpl::DebugCommandBreakpointSource:
-      return ":debug";
-    case V8DebuggerAgentImpl::MonitorCommandBreakpointSource:
-      return ":monitor";
-  }
-  return String16();
-}
-
-String16 generateBreakpointId(const ScriptBreakpoint& breakpoint,
-                              V8DebuggerAgentImpl::BreakpointSource source) {
+String16 generateBreakpointId(BreakpointType type,
+                              const String16& scriptSelector, int lineNumber,
+                              int columnNumber) {
   String16Builder builder;
-  builder.append(breakpoint.script_id);
+  builder.appendNumber(static_cast<int>(type));
   builder.append(':');
-  builder.appendNumber(breakpoint.line_number);
+  builder.appendNumber(lineNumber);
   builder.append(':');
-  builder.appendNumber(breakpoint.column_number);
-  builder.append(breakpointIdSuffix(source));
+  builder.appendNumber(columnNumber);
+  builder.append(':');
+  builder.append(scriptSelector);
   return builder.toString();
 }
 
+String16 generateBreakpointId(BreakpointType type,
+                              v8::Local<v8::Function> function) {
+  String16Builder builder;
+  builder.appendNumber(static_cast<int>(type));
+  builder.append(':');
+  builder.appendNumber(v8::debug::GetDebuggingId(function));
+  return builder.toString();
+}
+
+String16 generateInstrumentationBreakpointId(const String16& instrumentation) {
+  String16Builder builder;
+  builder.appendNumber(
+      static_cast<int>(BreakpointType::kInstrumentationBreakpoint));
+  builder.append(':');
+  builder.append(instrumentation);
+  return builder.toString();
+}
+
+bool parseBreakpointId(const String16& breakpointId, BreakpointType* type,
+                       String16* scriptSelector = nullptr,
+                       int* lineNumber = nullptr, int* columnNumber = nullptr) {
+  size_t typeLineSeparator = breakpointId.find(':');
+  if (typeLineSeparator == String16::kNotFound) return false;
+
+  int rawType = breakpointId.substring(0, typeLineSeparator).toInteger();
+  if (rawType < static_cast<int>(BreakpointType::kByUrl) ||
+      rawType > static_cast<int>(BreakpointType::kInstrumentationBreakpoint)) {
+    return false;
+  }
+  if (type) *type = static_cast<BreakpointType>(rawType);
+  if (rawType == static_cast<int>(BreakpointType::kDebugCommand) ||
+      rawType == static_cast<int>(BreakpointType::kMonitorCommand) ||
+      rawType == static_cast<int>(BreakpointType::kBreakpointAtEntry) ||
+      rawType == static_cast<int>(BreakpointType::kInstrumentationBreakpoint)) {
+    // The script and source position are not encoded in this case.
+    return true;
+  }
+
+  size_t lineColumnSeparator = breakpointId.find(':', typeLineSeparator + 1);
+  if (lineColumnSeparator == String16::kNotFound) return false;
+  size_t columnSelectorSeparator =
+      breakpointId.find(':', lineColumnSeparator + 1);
+  if (columnSelectorSeparator == String16::kNotFound) return false;
+  if (scriptSelector) {
+    *scriptSelector = breakpointId.substring(columnSelectorSeparator + 1);
+  }
+  if (lineNumber) {
+    *lineNumber = breakpointId
+                      .substring(typeLineSeparator + 1,
+                                 lineColumnSeparator - typeLineSeparator - 1)
+                      .toInteger();
+  }
+  if (columnNumber) {
+    *columnNumber =
+        breakpointId
+            .substring(lineColumnSeparator + 1,
+                       columnSelectorSeparator - lineColumnSeparator - 1)
+            .toInteger();
+  }
+  return true;
+}
+
 bool positionComparator(const std::pair<int, int>& a,
                         const std::pair<int, int>& b) {
   if (a.first != b.first) return a.first < b.first;
   return a.second < b.second;
 }
 
-std::unique_ptr<protocol::Debugger::Location> buildProtocolLocation(
-    const String16& scriptId, int lineNumber, int columnNumber) {
-  return protocol::Debugger::Location::create()
-      .setScriptId(scriptId)
-      .setLineNumber(lineNumber)
-      .setColumnNumber(columnNumber)
-      .build();
+String16 breakpointHint(const V8DebuggerScript& script, int lineNumber,
+                        int columnNumber) {
+  int offset = script.offset(lineNumber, columnNumber);
+  if (offset == V8DebuggerScript::kNoOffset) return String16();
+  String16 hint =
+      script.source(offset, kBreakpointHintMaxLength).stripWhiteSpace();
+  for (size_t i = 0; i < hint.length(); ++i) {
+    if (hint[i] == '\r' || hint[i] == '\n' || hint[i] == ';') {
+      return hint.substring(0, i);
+    }
+  }
+  return hint;
 }
 
+void adjustBreakpointLocation(const V8DebuggerScript& script,
+                              const String16& hint, int* lineNumber,
+                              int* columnNumber) {
+  if (*lineNumber < script.startLine() || *lineNumber > script.endLine())
+    return;
+  if (hint.isEmpty()) return;
+  intptr_t sourceOffset = script.offset(*lineNumber, *columnNumber);
+  if (sourceOffset == V8DebuggerScript::kNoOffset) return;
+
+  intptr_t searchRegionOffset = std::max(
+      sourceOffset - kBreakpointHintMaxSearchOffset, static_cast<intptr_t>(0));
+  size_t offset = sourceOffset - searchRegionOffset;
+  String16 searchArea = script.source(searchRegionOffset,
+                                      offset + kBreakpointHintMaxSearchOffset);
+
+  size_t nextMatch = searchArea.find(hint, offset);
+  size_t prevMatch = searchArea.reverseFind(hint, offset);
+  if (nextMatch == String16::kNotFound && prevMatch == String16::kNotFound) {
+    return;
+  }
+  size_t bestMatch;
+  if (nextMatch == String16::kNotFound) {
+    bestMatch = prevMatch;
+  } else if (prevMatch == String16::kNotFound) {
+    bestMatch = nextMatch;
+  } else {
+    bestMatch = nextMatch - offset < offset - prevMatch ? nextMatch : prevMatch;
+  }
+  bestMatch += searchRegionOffset;
+  v8::debug::Location hintPosition =
+      script.location(static_cast<int>(bestMatch));
+  if (hintPosition.IsEmpty()) return;
+  *lineNumber = hintPosition.GetLineNumber();
+  *columnNumber = hintPosition.GetColumnNumber();
+}
+
+String16 breakLocationType(v8::debug::BreakLocationType type) {
+  switch (type) {
+    case v8::debug::kCallBreakLocation:
+      return protocol::Debugger::BreakLocation::TypeEnum::Call;
+    case v8::debug::kReturnBreakLocation:
+      return protocol::Debugger::BreakLocation::TypeEnum::Return;
+    case v8::debug::kDebuggerStatementBreakLocation:
+      return protocol::Debugger::BreakLocation::TypeEnum::DebuggerStatement;
+    case v8::debug::kCommonBreakLocation:
+      return String16();
+  }
+  return String16();
+}
+
+String16 scopeType(v8::debug::ScopeIterator::ScopeType type) {
+  switch (type) {
+    case v8::debug::ScopeIterator::ScopeTypeGlobal:
+      return Scope::TypeEnum::Global;
+    case v8::debug::ScopeIterator::ScopeTypeLocal:
+      return Scope::TypeEnum::Local;
+    case v8::debug::ScopeIterator::ScopeTypeWith:
+      return Scope::TypeEnum::With;
+    case v8::debug::ScopeIterator::ScopeTypeClosure:
+      return Scope::TypeEnum::Closure;
+    case v8::debug::ScopeIterator::ScopeTypeCatch:
+      return Scope::TypeEnum::Catch;
+    case v8::debug::ScopeIterator::ScopeTypeBlock:
+      return Scope::TypeEnum::Block;
+    case v8::debug::ScopeIterator::ScopeTypeScript:
+      return Scope::TypeEnum::Script;
+    case v8::debug::ScopeIterator::ScopeTypeEval:
+      return Scope::TypeEnum::Eval;
+    case v8::debug::ScopeIterator::ScopeTypeModule:
+      return Scope::TypeEnum::Module;
+    case v8::debug::ScopeIterator::ScopeTypeWasmExpressionStack:
+      return Scope::TypeEnum::WasmExpressionStack;
+  }
+  UNREACHABLE();
+  return String16();
+}
+
+Response buildScopes(v8::Isolate* isolate, v8::debug::ScopeIterator* iterator,
+                     InjectedScript* injectedScript,
+                     std::unique_ptr<Array<Scope>>* scopes) {
+  *scopes = std::make_unique<Array<Scope>>();
+  if (!injectedScript) return Response::Success();
+  if (iterator->Done()) return Response::Success();
+
+  String16 scriptId = String16::fromInteger(iterator->GetScriptId());
+
+  for (; !iterator->Done(); iterator->Advance()) {
+    std::unique_ptr<RemoteObject> object;
+    Response result =
+        injectedScript->wrapObject(iterator->GetObject(), kBacktraceObjectGroup,
+                                   WrapMode::kNoPreview, &object);
+    if (!result.IsSuccess()) return result;
+
+    auto scope = Scope::create()
+                     .setType(scopeType(iterator->GetType()))
+                     .setObject(std::move(object))
+                     .build();
+
+    String16 name = toProtocolStringWithTypeCheck(
+        isolate, iterator->GetFunctionDebugName());
+    if (!name.isEmpty()) scope->setName(name);
+
+    if (iterator->HasLocationInfo()) {
+      v8::debug::Location start = iterator->GetStartLocation();
+      scope->setStartLocation(protocol::Debugger::Location::create()
+                                  .setScriptId(scriptId)
+                                  .setLineNumber(start.GetLineNumber())
+                                  .setColumnNumber(start.GetColumnNumber())
+                                  .build());
+
+      v8::debug::Location end = iterator->GetEndLocation();
+      scope->setEndLocation(protocol::Debugger::Location::create()
+                                .setScriptId(scriptId)
+                                .setLineNumber(end.GetLineNumber())
+                                .setColumnNumber(end.GetColumnNumber())
+                                .build());
+    }
+    (*scopes)->emplace_back(std::move(scope));
+  }
+  return Response::Success();
+}
+
+protocol::DictionaryValue* getOrCreateObject(protocol::DictionaryValue* object,
+                                             const String16& key) {
+  protocol::DictionaryValue* value = object->getObject(key);
+  if (value) return value;
+  std::unique_ptr<protocol::DictionaryValue> newDictionary =
+      protocol::DictionaryValue::create();
+  value = newDictionary.get();
+  object->setObject(key, std::move(newDictionary));
+  return value;
+}
+
+Response isValidPosition(protocol::Debugger::ScriptPosition* position) {
+  if (position->getLineNumber() < 0)
+    return Response::ServerError("Position missing 'line' or 'line' < 0.");
+  if (position->getColumnNumber() < 0)
+    return Response::ServerError("Position missing 'column' or 'column' < 0.");
+  return Response::Success();
+}
+
+Response isValidRangeOfPositions(std::vector<std::pair<int, int>>& positions) {
+  for (size_t i = 1; i < positions.size(); ++i) {
+    if (positions[i - 1].first < positions[i].first) continue;
+    if (positions[i - 1].first == positions[i].first &&
+        positions[i - 1].second < positions[i].second)
+      continue;
+    return Response::ServerError(
+        "Input positions array is not sorted or contains duplicate values.");
+  }
+  return Response::Success();
+}
 }  // namespace
 
 V8DebuggerAgentImpl::V8DebuggerAgentImpl(
@@ -130,70 +349,83 @@
       m_enabled(false),
       m_state(state),
       m_frontend(frontendChannel),
-      m_isolate(m_inspector->isolate()),
-      m_scheduledDebuggerStep(NoStep),
-      m_javaScriptPauseScheduled(false),
-      m_recursionLevelForStepOut(0) {
-}
+      m_isolate(m_inspector->isolate()) {}
 
-V8DebuggerAgentImpl::~V8DebuggerAgentImpl() {}
+V8DebuggerAgentImpl::~V8DebuggerAgentImpl() = default;
 
 void V8DebuggerAgentImpl::enableImpl() {
   m_enabled = true;
   m_state->setBoolean(DebuggerAgentState::debuggerEnabled, true);
   m_debugger->enable();
 
-  std::vector<std::unique_ptr<V8DebuggerScript>> compiledScripts;
-  m_debugger->getCompiledScripts(m_session->contextGroupId(), compiledScripts);
-  for (size_t i = 0; i < compiledScripts.size(); i++)
-    didParseSource(std::move(compiledScripts[i]), true);
+  std::vector<std::unique_ptr<V8DebuggerScript>> compiledScripts =
+      m_debugger->getCompiledScripts(m_session->contextGroupId(), this);
+  for (auto& script : compiledScripts) {
+    didParseSource(std::move(script), true);
+  }
 
-  // FIXME(WK44513): breakpoints activated flag should be synchronized between
-  // all front-ends
-  m_debugger->setBreakpointsActivated(true);
+  m_breakpointsActive = true;
+  m_debugger->setBreakpointsActive(true);
+
+  if (isPaused()) {
+    didPause(0, v8::Local<v8::Value>(), std::vector<v8::debug::BreakpointId>(),
+             v8::debug::kException, false, false, false);
+  }
 }
 
-bool V8DebuggerAgentImpl::enabled() { return m_enabled; }
-
-Response V8DebuggerAgentImpl::enable() {
-  if (enabled()) return Response::OK();
+Response V8DebuggerAgentImpl::enable(Maybe<double> maxScriptsCacheSize,
+                                     String16* outDebuggerId) {
+  m_maxScriptCacheSize = v8::base::saturated_cast<size_t>(
+      maxScriptsCacheSize.fromMaybe(std::numeric_limits<double>::max()));
+  *outDebuggerId =
+      m_debugger->debuggerIdFor(m_session->contextGroupId()).toString();
+  if (enabled()) return Response::Success();
 
   if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId()))
-    return Response::Error("Script execution is prohibited");
+    return Response::ServerError("Script execution is prohibited");
 
   enableImpl();
-  return Response::OK();
+  return Response::Success();
 }
 
 Response V8DebuggerAgentImpl::disable() {
-  if (!enabled()) return Response::OK();
+  if (!enabled()) return Response::Success();
 
-  m_state->setObject(DebuggerAgentState::javaScriptBreakpoints,
-                     protocol::DictionaryValue::create());
+  m_state->remove(DebuggerAgentState::breakpointsByRegex);
+  m_state->remove(DebuggerAgentState::breakpointsByUrl);
+  m_state->remove(DebuggerAgentState::breakpointsByScriptHash);
+  m_state->remove(DebuggerAgentState::breakpointHints);
+  m_state->remove(DebuggerAgentState::instrumentationBreakpoints);
+
   m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState,
                       v8::debug::NoBreakOnException);
   m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, 0);
 
-  if (isPaused()) m_debugger->continueProgram();
-  m_debugger->disable();
-  JavaScriptCallFrames emptyCallFrames;
-  m_pausedCallFrames.swap(emptyCallFrames);
+  if (m_breakpointsActive) {
+    m_debugger->setBreakpointsActive(false);
+    m_breakpointsActive = false;
+  }
   m_blackboxedPositions.clear();
   m_blackboxPattern.reset();
   resetBlackboxedStateCache();
+  m_skipList.clear();
   m_scripts.clear();
+  m_cachedScriptIds.clear();
+  m_cachedScriptSize = 0;
+  for (const auto& it : m_debuggerBreakpointIdToBreakpointId) {
+    v8::debug::RemoveBreakpoint(m_isolate, it.first);
+  }
   m_breakpointIdToDebuggerBreakpointIds.clear();
+  m_debuggerBreakpointIdToBreakpointId.clear();
   m_debugger->setAsyncCallStackDepth(this, 0);
-  m_continueToLocationBreakpointId = String16();
   clearBreakDetails();
-  m_scheduledDebuggerStep = NoStep;
-  m_javaScriptPauseScheduled = false;
   m_skipAllPauses = false;
   m_state->setBoolean(DebuggerAgentState::skipAllPauses, false);
   m_state->remove(DebuggerAgentState::blackboxPattern);
   m_enabled = false;
   m_state->setBoolean(DebuggerAgentState::debuggerEnabled, false);
-  return Response::OK();
+  m_debugger->disable();
+  return Response::Success();
 }
 
 void V8DebuggerAgentImpl::restore() {
@@ -225,148 +457,273 @@
 }
 
 Response V8DebuggerAgentImpl::setBreakpointsActive(bool active) {
-  if (!enabled()) return Response::Error(kDebuggerNotEnabled);
-  m_debugger->setBreakpointsActivated(active);
-  return Response::OK();
+  if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
+  if (m_breakpointsActive == active) return Response::Success();
+  m_breakpointsActive = active;
+  m_debugger->setBreakpointsActive(active);
+  if (!active && !m_breakReason.empty()) {
+    clearBreakDetails();
+    m_debugger->setPauseOnNextCall(false, m_session->contextGroupId());
+  }
+  return Response::Success();
 }
 
 Response V8DebuggerAgentImpl::setSkipAllPauses(bool skip) {
   m_state->setBoolean(DebuggerAgentState::skipAllPauses, skip);
   m_skipAllPauses = skip;
-  return Response::OK();
+  return Response::Success();
 }
 
-static std::unique_ptr<protocol::DictionaryValue>
-buildObjectForBreakpointCookie(const String16& url, int lineNumber,
-                               int columnNumber, const String16& condition,
-                               bool isRegex) {
-  std::unique_ptr<protocol::DictionaryValue> breakpointObject =
-      protocol::DictionaryValue::create();
-  breakpointObject->setString(DebuggerAgentState::url, url);
-  breakpointObject->setInteger(DebuggerAgentState::lineNumber, lineNumber);
-  breakpointObject->setInteger(DebuggerAgentState::columnNumber, columnNumber);
-  breakpointObject->setString(DebuggerAgentState::condition, condition);
-  breakpointObject->setBoolean(DebuggerAgentState::isRegex, isRegex);
-  return breakpointObject;
-}
-
-static bool matches(V8InspectorImpl* inspector, const String16& url,
-                    const String16& pattern, bool isRegex) {
-  if (isRegex) {
-    V8Regex regex(inspector, pattern, true);
-    return regex.match(url) != -1;
+static bool matches(V8InspectorImpl* inspector, const V8DebuggerScript& script,
+                    BreakpointType type, const String16& selector) {
+  switch (type) {
+    case BreakpointType::kByUrl:
+      return script.sourceURL() == selector;
+    case BreakpointType::kByScriptHash:
+      return script.hash() == selector;
+    case BreakpointType::kByUrlRegex: {
+      V8Regex regex(inspector, selector, true);
+      return regex.match(script.sourceURL()) != -1;
+    }
+    case BreakpointType::kByScriptId: {
+      return script.scriptId() == selector;
+    }
+    default:
+      return false;
   }
-  return url == pattern;
 }
 
 Response V8DebuggerAgentImpl::setBreakpointByUrl(
     int lineNumber, Maybe<String16> optionalURL,
-    Maybe<String16> optionalURLRegex, Maybe<int> optionalColumnNumber,
-    Maybe<String16> optionalCondition, String16* outBreakpointId,
+    Maybe<String16> optionalURLRegex, Maybe<String16> optionalScriptHash,
+    Maybe<int> optionalColumnNumber, Maybe<String16> optionalCondition,
+    String16* outBreakpointId,
     std::unique_ptr<protocol::Array<protocol::Debugger::Location>>* locations) {
-  *locations = Array<protocol::Debugger::Location>::create();
-  if (optionalURL.isJust() == optionalURLRegex.isJust())
-    return Response::Error("Either url or urlRegex must be specified.");
+  *locations = std::make_unique<Array<protocol::Debugger::Location>>();
 
-  String16 url = optionalURL.isJust() ? optionalURL.fromJust()
-                                      : optionalURLRegex.fromJust();
+  int specified = (optionalURL.isJust() ? 1 : 0) +
+                  (optionalURLRegex.isJust() ? 1 : 0) +
+                  (optionalScriptHash.isJust() ? 1 : 0);
+  if (specified != 1) {
+    return Response::ServerError(
+        "Either url or urlRegex or scriptHash must be specified.");
+  }
   int columnNumber = 0;
   if (optionalColumnNumber.isJust()) {
     columnNumber = optionalColumnNumber.fromJust();
-    if (columnNumber < 0) return Response::Error("Incorrect column number");
+    if (columnNumber < 0)
+      return Response::ServerError("Incorrect column number");
   }
-  String16 condition = optionalCondition.fromMaybe("");
-  bool isRegex = optionalURLRegex.isJust();
 
-  String16 breakpointId = (isRegex ? "/" + url + "/" : url) + ":" +
-                          String16::fromInteger(lineNumber) + ":" +
-                          String16::fromInteger(columnNumber);
-  protocol::DictionaryValue* breakpointsCookie =
-      m_state->getObject(DebuggerAgentState::javaScriptBreakpoints);
-  if (!breakpointsCookie) {
-    std::unique_ptr<protocol::DictionaryValue> newValue =
-        protocol::DictionaryValue::create();
-    breakpointsCookie = newValue.get();
-    m_state->setObject(DebuggerAgentState::javaScriptBreakpoints,
-                       std::move(newValue));
+  BreakpointType type = BreakpointType::kByUrl;
+  String16 selector;
+  if (optionalURLRegex.isJust()) {
+    selector = optionalURLRegex.fromJust();
+    type = BreakpointType::kByUrlRegex;
+  } else if (optionalURL.isJust()) {
+    selector = optionalURL.fromJust();
+    type = BreakpointType::kByUrl;
+  } else if (optionalScriptHash.isJust()) {
+    selector = optionalScriptHash.fromJust();
+    type = BreakpointType::kByScriptHash;
   }
-  if (breakpointsCookie->get(breakpointId))
-    return Response::Error("Breakpoint at specified location already exists.");
 
-  breakpointsCookie->setObject(
-      breakpointId, buildObjectForBreakpointCookie(
-                        url, lineNumber, columnNumber, condition, isRegex));
+  String16 condition = optionalCondition.fromMaybe(String16());
+  String16 breakpointId =
+      generateBreakpointId(type, selector, lineNumber, columnNumber);
+  protocol::DictionaryValue* breakpoints;
+  switch (type) {
+    case BreakpointType::kByUrlRegex:
+      breakpoints =
+          getOrCreateObject(m_state, DebuggerAgentState::breakpointsByRegex);
+      break;
+    case BreakpointType::kByUrl:
+      breakpoints = getOrCreateObject(
+          getOrCreateObject(m_state, DebuggerAgentState::breakpointsByUrl),
+          selector);
+      break;
+    case BreakpointType::kByScriptHash:
+      breakpoints = getOrCreateObject(
+          getOrCreateObject(m_state,
+                            DebuggerAgentState::breakpointsByScriptHash),
+          selector);
+      break;
+    default:
+      UNREACHABLE();
+  }
+  if (breakpoints->get(breakpointId)) {
+    return Response::ServerError(
+        "Breakpoint at specified location already exists.");
+  }
 
-  ScriptBreakpoint breakpoint(String16(), lineNumber, columnNumber, condition);
+  String16 hint;
   for (const auto& script : m_scripts) {
-    if (!matches(m_inspector, script.second->sourceURL(), url, isRegex))
-      continue;
-    breakpoint.script_id = script.first;
-    std::unique_ptr<protocol::Debugger::Location> location =
-        resolveBreakpoint(breakpointId, breakpoint, UserBreakpointSource);
-    if (location) (*locations)->addItem(std::move(location));
+    if (!matches(m_inspector, *script.second, type, selector)) continue;
+    if (!hint.isEmpty()) {
+      adjustBreakpointLocation(*script.second, hint, &lineNumber,
+                               &columnNumber);
+    }
+    std::unique_ptr<protocol::Debugger::Location> location = setBreakpointImpl(
+        breakpointId, script.first, condition, lineNumber, columnNumber);
+    if (location && type != BreakpointType::kByUrlRegex) {
+      hint = breakpointHint(*script.second, lineNumber, columnNumber);
+    }
+    if (location) (*locations)->emplace_back(std::move(location));
   }
-
+  breakpoints->setString(breakpointId, condition);
+  if (!hint.isEmpty()) {
+    protocol::DictionaryValue* breakpointHints =
+        getOrCreateObject(m_state, DebuggerAgentState::breakpointHints);
+    breakpointHints->setString(breakpointId, hint);
+  }
   *outBreakpointId = breakpointId;
-  return Response::OK();
+  return Response::Success();
 }
 
 Response V8DebuggerAgentImpl::setBreakpoint(
     std::unique_ptr<protocol::Debugger::Location> location,
     Maybe<String16> optionalCondition, String16* outBreakpointId,
     std::unique_ptr<protocol::Debugger::Location>* actualLocation) {
-  ScriptBreakpoint breakpoint(
-      location->getScriptId(), location->getLineNumber(),
-      location->getColumnNumber(0), optionalCondition.fromMaybe(String16()));
-
-  String16 breakpointId =
-      generateBreakpointId(breakpoint, UserBreakpointSource);
+  String16 breakpointId = generateBreakpointId(
+      BreakpointType::kByScriptId, location->getScriptId(),
+      location->getLineNumber(), location->getColumnNumber(0));
   if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
       m_breakpointIdToDebuggerBreakpointIds.end()) {
-    return Response::Error("Breakpoint at specified location already exists.");
+    return Response::ServerError(
+        "Breakpoint at specified location already exists.");
   }
-  *actualLocation =
-      resolveBreakpoint(breakpointId, breakpoint, UserBreakpointSource);
-  if (!*actualLocation) return Response::Error("Could not resolve breakpoint");
+  *actualLocation = setBreakpointImpl(breakpointId, location->getScriptId(),
+                                      optionalCondition.fromMaybe(String16()),
+                                      location->getLineNumber(),
+                                      location->getColumnNumber(0));
+  if (!*actualLocation)
+    return Response::ServerError("Could not resolve breakpoint");
   *outBreakpointId = breakpointId;
-  return Response::OK();
+  return Response::Success();
+}
+
+Response V8DebuggerAgentImpl::setBreakpointOnFunctionCall(
+    const String16& functionObjectId, Maybe<String16> optionalCondition,
+    String16* outBreakpointId) {
+  InjectedScript::ObjectScope scope(m_session, functionObjectId);
+  Response response = scope.initialize();
+  if (!response.IsSuccess()) return response;
+  if (!scope.object()->IsFunction()) {
+    return Response::ServerError("Could not find function with given id");
+  }
+  v8::Local<v8::Function> function =
+      v8::Local<v8::Function>::Cast(scope.object());
+  String16 breakpointId =
+      generateBreakpointId(BreakpointType::kBreakpointAtEntry, function);
+  if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
+      m_breakpointIdToDebuggerBreakpointIds.end()) {
+    return Response::ServerError(
+        "Breakpoint at specified location already exists.");
+  }
+  v8::Local<v8::String> condition =
+      toV8String(m_isolate, optionalCondition.fromMaybe(String16()));
+  setBreakpointImpl(breakpointId, function, condition);
+  *outBreakpointId = breakpointId;
+  return Response::Success();
+}
+
+Response V8DebuggerAgentImpl::setInstrumentationBreakpoint(
+    const String16& instrumentation, String16* outBreakpointId) {
+  if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
+  String16 breakpointId = generateInstrumentationBreakpointId(instrumentation);
+  protocol::DictionaryValue* breakpoints = getOrCreateObject(
+      m_state, DebuggerAgentState::instrumentationBreakpoints);
+  if (breakpoints->get(breakpointId)) {
+    return Response::ServerError(
+        "Instrumentation breakpoint is already enabled.");
+  }
+  breakpoints->setBoolean(breakpointId, true);
+  *outBreakpointId = breakpointId;
+  return Response::Success();
 }
 
 Response V8DebuggerAgentImpl::removeBreakpoint(const String16& breakpointId) {
-  if (!enabled()) return Response::Error(kDebuggerNotEnabled);
-  protocol::DictionaryValue* breakpointsCookie =
-      m_state->getObject(DebuggerAgentState::javaScriptBreakpoints);
-  if (breakpointsCookie) breakpointsCookie->remove(breakpointId);
-  removeBreakpointImpl(breakpointId);
-  return Response::OK();
+  if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
+  BreakpointType type;
+  String16 selector;
+  if (!parseBreakpointId(breakpointId, &type, &selector)) {
+    return Response::Success();
+  }
+  protocol::DictionaryValue* breakpoints = nullptr;
+  switch (type) {
+    case BreakpointType::kByUrl: {
+      protocol::DictionaryValue* breakpointsByUrl =
+          m_state->getObject(DebuggerAgentState::breakpointsByUrl);
+      if (breakpointsByUrl) {
+        breakpoints = breakpointsByUrl->getObject(selector);
+      }
+    } break;
+    case BreakpointType::kByScriptHash: {
+      protocol::DictionaryValue* breakpointsByScriptHash =
+          m_state->getObject(DebuggerAgentState::breakpointsByScriptHash);
+      if (breakpointsByScriptHash) {
+        breakpoints = breakpointsByScriptHash->getObject(selector);
+      }
+    } break;
+    case BreakpointType::kByUrlRegex:
+      breakpoints = m_state->getObject(DebuggerAgentState::breakpointsByRegex);
+      break;
+    case BreakpointType::kInstrumentationBreakpoint:
+      breakpoints =
+          m_state->getObject(DebuggerAgentState::instrumentationBreakpoints);
+      break;
+    default:
+      break;
+  }
+  if (breakpoints) breakpoints->remove(breakpointId);
+  protocol::DictionaryValue* breakpointHints =
+      m_state->getObject(DebuggerAgentState::breakpointHints);
+  if (breakpointHints) breakpointHints->remove(breakpointId);
+
+  // Get a list of scripts to remove breakpoints.
+  // TODO(duongn): we can do better here if from breakpoint id we can tell it is
+  // not Wasm breakpoint.
+  std::vector<V8DebuggerScript*> scripts;
+  for (const auto& scriptIter : m_scripts) {
+    if (!matches(m_inspector, *scriptIter.second, type, selector)) continue;
+    V8DebuggerScript* script = scriptIter.second.get();
+    scripts.push_back(script);
+  }
+  removeBreakpointImpl(breakpointId, scripts);
+
+  return Response::Success();
 }
 
-void V8DebuggerAgentImpl::removeBreakpointImpl(const String16& breakpointId) {
+void V8DebuggerAgentImpl::removeBreakpointImpl(
+    const String16& breakpointId,
+    const std::vector<V8DebuggerScript*>& scripts) {
   DCHECK(enabled());
   BreakpointIdToDebuggerBreakpointIdsMap::iterator
       debuggerBreakpointIdsIterator =
           m_breakpointIdToDebuggerBreakpointIds.find(breakpointId);
   if (debuggerBreakpointIdsIterator ==
-      m_breakpointIdToDebuggerBreakpointIds.end())
+      m_breakpointIdToDebuggerBreakpointIds.end()) {
     return;
-  const std::vector<String16>& ids = debuggerBreakpointIdsIterator->second;
-  for (size_t i = 0; i < ids.size(); ++i) {
-    const String16& debuggerBreakpointId = ids[i];
-
-    m_debugger->removeBreakpoint(debuggerBreakpointId);
-    m_serverBreakpoints.erase(debuggerBreakpointId);
+  }
+  for (const auto& id : debuggerBreakpointIdsIterator->second) {
+    for (auto& script : scripts) {
+      script->removeWasmBreakpoint(id);
+    }
+    v8::debug::RemoveBreakpoint(m_isolate, id);
+    m_debuggerBreakpointIdToBreakpointId.erase(id);
   }
   m_breakpointIdToDebuggerBreakpointIds.erase(breakpointId);
 }
 
 Response V8DebuggerAgentImpl::getPossibleBreakpoints(
     std::unique_ptr<protocol::Debugger::Location> start,
-    Maybe<protocol::Debugger::Location> end,
-    std::unique_ptr<protocol::Array<protocol::Debugger::Location>>* locations) {
+    Maybe<protocol::Debugger::Location> end, Maybe<bool> restrictToFunction,
+    std::unique_ptr<protocol::Array<protocol::Debugger::BreakLocation>>*
+        locations) {
   String16 scriptId = start->getScriptId();
 
   if (start->getLineNumber() < 0 || start->getColumnNumber(0) < 0)
-    return Response::Error(
+    return Response::ServerError(
         "start.lineNumber and start.columnNumber should be >= 0");
 
   v8::debug::Location v8Start(start->getLineNumber(),
@@ -374,49 +731,104 @@
   v8::debug::Location v8End;
   if (end.isJust()) {
     if (end.fromJust()->getScriptId() != scriptId)
-      return Response::Error("Locations should contain the same scriptId");
+      return Response::ServerError(
+          "Locations should contain the same scriptId");
     int line = end.fromJust()->getLineNumber();
     int column = end.fromJust()->getColumnNumber(0);
     if (line < 0 || column < 0)
-      return Response::Error(
+      return Response::ServerError(
           "end.lineNumber and end.columnNumber should be >= 0");
     v8End = v8::debug::Location(line, column);
   }
   auto it = m_scripts.find(scriptId);
-  if (it == m_scripts.end()) return Response::Error("Script not found");
-
-  std::vector<v8::debug::Location> v8Locations;
-  if (!it->second->getPossibleBreakpoints(v8Start, v8End, &v8Locations))
-    return Response::InternalError();
-
-  *locations = protocol::Array<protocol::Debugger::Location>::create();
-  for (size_t i = 0; i < v8Locations.size(); ++i) {
-    (*locations)
-        ->addItem(protocol::Debugger::Location::create()
-                      .setScriptId(scriptId)
-                      .setLineNumber(v8Locations[i].GetLineNumber())
-                      .setColumnNumber(v8Locations[i].GetColumnNumber())
-                      .build());
+  if (it == m_scripts.end()) return Response::ServerError("Script not found");
+  std::vector<v8::debug::BreakLocation> v8Locations;
+  {
+    v8::HandleScope handleScope(m_isolate);
+    int contextId = it->second->executionContextId();
+    InspectedContext* inspected = m_inspector->getContext(contextId);
+    if (!inspected) {
+      return Response::ServerError("Cannot retrive script context");
+    }
+    v8::Context::Scope contextScope(inspected->context());
+    v8::MicrotasksScope microtasks(m_isolate,
+                                   v8::MicrotasksScope::kDoNotRunMicrotasks);
+    v8::TryCatch tryCatch(m_isolate);
+    it->second->getPossibleBreakpoints(
+        v8Start, v8End, restrictToFunction.fromMaybe(false), &v8Locations);
   }
-  return Response::OK();
+
+  *locations =
+      std::make_unique<protocol::Array<protocol::Debugger::BreakLocation>>();
+
+  // TODO(1106269): Return an error instead of capping the number of
+  // breakpoints.
+  const size_t numBreakpointsToSend =
+      std::min(v8Locations.size(), kMaxNumBreakpoints);
+  for (size_t i = 0; i < numBreakpointsToSend; ++i) {
+    std::unique_ptr<protocol::Debugger::BreakLocation> breakLocation =
+        protocol::Debugger::BreakLocation::create()
+            .setScriptId(scriptId)
+            .setLineNumber(v8Locations[i].GetLineNumber())
+            .setColumnNumber(v8Locations[i].GetColumnNumber())
+            .build();
+    if (v8Locations[i].type() != v8::debug::kCommonBreakLocation) {
+      breakLocation->setType(breakLocationType(v8Locations[i].type()));
+    }
+    (*locations)->emplace_back(std::move(breakLocation));
+  }
+  return Response::Success();
 }
 
 Response V8DebuggerAgentImpl::continueToLocation(
-    std::unique_ptr<protocol::Debugger::Location> location) {
-  if (!enabled()) return Response::Error(kDebuggerNotEnabled);
-  if (!m_continueToLocationBreakpointId.isEmpty()) {
-    m_debugger->removeBreakpoint(m_continueToLocationBreakpointId);
-    m_continueToLocationBreakpointId = "";
+    std::unique_ptr<protocol::Debugger::Location> location,
+    Maybe<String16> targetCallFrames) {
+  if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
+  if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
+  ScriptsMap::iterator it = m_scripts.find(location->getScriptId());
+  if (it == m_scripts.end()) {
+    return Response::ServerError("Cannot continue to specified location");
   }
+  V8DebuggerScript* script = it->second.get();
+  int contextId = script->executionContextId();
+  InspectedContext* inspected = m_inspector->getContext(contextId);
+  if (!inspected)
+    return Response::ServerError("Cannot continue to specified location");
+  v8::HandleScope handleScope(m_isolate);
+  v8::Context::Scope contextScope(inspected->context());
+  return m_debugger->continueToLocation(
+      m_session->contextGroupId(), script, std::move(location),
+      targetCallFrames.fromMaybe(
+          protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any));
+}
 
-  ScriptBreakpoint breakpoint(location->getScriptId(),
-                              location->getLineNumber(),
-                              location->getColumnNumber(0), String16());
+Response V8DebuggerAgentImpl::getStackTrace(
+    std::unique_ptr<protocol::Runtime::StackTraceId> inStackTraceId,
+    std::unique_ptr<protocol::Runtime::StackTrace>* outStackTrace) {
+  bool isOk = false;
+  int64_t id = inStackTraceId->getId().toInteger64(&isOk);
+  if (!isOk) return Response::ServerError("Invalid stack trace id");
 
-  m_continueToLocationBreakpointId = m_debugger->setBreakpoint(
-      breakpoint, &breakpoint.line_number, &breakpoint.column_number);
-  // TODO(kozyatinskiy): Return actual line and column number.
-  return resume();
+  V8DebuggerId debuggerId;
+  if (inStackTraceId->hasDebuggerId()) {
+    debuggerId = V8DebuggerId(inStackTraceId->getDebuggerId(String16()));
+  } else {
+    debuggerId = m_debugger->debuggerIdFor(m_session->contextGroupId());
+  }
+  if (!debuggerId.isValid())
+    return Response::ServerError("Invalid stack trace id");
+
+  V8StackTraceId v8StackTraceId(id, debuggerId.pair());
+  if (v8StackTraceId.IsInvalid())
+    return Response::ServerError("Invalid stack trace id");
+  auto stack =
+      m_debugger->stackTraceFor(m_session->contextGroupId(), v8StackTraceId);
+  if (!stack) {
+    return Response::ServerError("Stack trace with given id is not found");
+  }
+  *outStackTrace = stack->buildInspectorObject(
+      m_debugger, m_debugger->maxAsyncCallChainDepth());
+  return Response::Success();
 }
 
 bool V8DebuggerAgentImpl::isFunctionBlackboxed(const String16& scriptId,
@@ -453,45 +865,87 @@
          std::distance(ranges.begin(), itStartRange) % 2;
 }
 
+bool V8DebuggerAgentImpl::shouldBeSkipped(const String16& scriptId, int line,
+                                          int column) {
+  if (m_skipList.empty()) return false;
+
+  auto it = m_skipList.find(scriptId);
+  if (it == m_skipList.end()) return false;
+
+  const std::vector<std::pair<int, int>>& ranges = it->second;
+  DCHECK(!ranges.empty());
+  const std::pair<int, int> location = std::make_pair(line, column);
+  auto itLowerBound = std::lower_bound(ranges.begin(), ranges.end(), location,
+                                       positionComparator);
+
+  bool shouldSkip = false;
+  if (itLowerBound != ranges.end()) {
+    // Skip lists are defined as pairs of locations that specify the
+    // start and the end of ranges to skip: [ranges[0], ranges[1], ..], where
+    // locations in [ranges[0], ranges[1]) should be skipped, i.e.
+    // [(lineStart, columnStart), (lineEnd, columnEnd)).
+    const bool isSameAsLowerBound = location == *itLowerBound;
+    const bool isUnevenIndex = (itLowerBound - ranges.begin()) % 2;
+    shouldSkip = isSameAsLowerBound ^ isUnevenIndex;
+  }
+
+  return shouldSkip;
+}
+
+bool V8DebuggerAgentImpl::acceptsPause(bool isOOMBreak) const {
+  return enabled() && (isOOMBreak || !m_skipAllPauses);
+}
+
 std::unique_ptr<protocol::Debugger::Location>
-V8DebuggerAgentImpl::resolveBreakpoint(const String16& breakpointId,
-                                       const ScriptBreakpoint& breakpoint,
-                                       BreakpointSource source) {
+V8DebuggerAgentImpl::setBreakpointImpl(const String16& breakpointId,
+                                       const String16& scriptId,
+                                       const String16& condition,
+                                       int lineNumber, int columnNumber) {
   v8::HandleScope handles(m_isolate);
   DCHECK(enabled());
-  // FIXME: remove these checks once crbug.com/520702 is resolved.
-  CHECK(!breakpointId.isEmpty());
-  CHECK(!breakpoint.script_id.isEmpty());
-  ScriptsMap::iterator scriptIterator = m_scripts.find(breakpoint.script_id);
+
+  ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
   if (scriptIterator == m_scripts.end()) return nullptr;
-  if (breakpoint.line_number < scriptIterator->second->startLine() ||
-      scriptIterator->second->endLine() < breakpoint.line_number)
+  V8DebuggerScript* script = scriptIterator->second.get();
+  if (lineNumber < script->startLine() || script->endLine() < lineNumber) {
     return nullptr;
+  }
 
-  // Translate from protocol location to v8 location for the debugger.
-  ScriptBreakpoint translatedBreakpoint = breakpoint;
-  m_debugger->wasmTranslation()->TranslateProtocolLocationToWasmScriptLocation(
-      &translatedBreakpoint.script_id, &translatedBreakpoint.line_number,
-      &translatedBreakpoint.column_number);
+  v8::debug::BreakpointId debuggerBreakpointId;
+  v8::debug::Location location(lineNumber, columnNumber);
+  int contextId = script->executionContextId();
+  InspectedContext* inspected = m_inspector->getContext(contextId);
+  if (!inspected) return nullptr;
 
-  int actualLineNumber;
-  int actualColumnNumber;
-  String16 debuggerBreakpointId = m_debugger->setBreakpoint(
-      translatedBreakpoint, &actualLineNumber, &actualColumnNumber);
-  if (debuggerBreakpointId.isEmpty()) return nullptr;
+  {
+    v8::Context::Scope contextScope(inspected->context());
+    if (!script->setBreakpoint(condition, &location, &debuggerBreakpointId)) {
+      return nullptr;
+    }
+  }
 
-  // Translate back from v8 location to protocol location for the return value.
-  m_debugger->wasmTranslation()->TranslateWasmScriptLocationToProtocolLocation(
-      &translatedBreakpoint.script_id, &actualLineNumber, &actualColumnNumber);
-
-  m_serverBreakpoints[debuggerBreakpointId] =
-      std::make_pair(breakpointId, source);
-  CHECK(!breakpointId.isEmpty());
-
+  m_debuggerBreakpointIdToBreakpointId[debuggerBreakpointId] = breakpointId;
   m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
       debuggerBreakpointId);
-  return buildProtocolLocation(translatedBreakpoint.script_id, actualLineNumber,
-                               actualColumnNumber);
+
+  return protocol::Debugger::Location::create()
+      .setScriptId(scriptId)
+      .setLineNumber(location.GetLineNumber())
+      .setColumnNumber(location.GetColumnNumber())
+      .build();
+}
+
+void V8DebuggerAgentImpl::setBreakpointImpl(const String16& breakpointId,
+                                            v8::Local<v8::Function> function,
+                                            v8::Local<v8::String> condition) {
+  v8::debug::BreakpointId debuggerBreakpointId;
+  if (!v8::debug::SetFunctionBreakpoint(function, condition,
+                                        &debuggerBreakpointId)) {
+    return;
+  }
+  m_debuggerBreakpointIdToBreakpointId[debuggerBreakpointId] = breakpointId;
+  m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
+      debuggerBreakpointId);
 }
 
 Response V8DebuggerAgentImpl::searchInContent(
@@ -501,90 +955,119 @@
   v8::HandleScope handles(m_isolate);
   ScriptsMap::iterator it = m_scripts.find(scriptId);
   if (it == m_scripts.end())
-    return Response::Error("No script for id: " + scriptId);
+    return Response::ServerError("No script for id: " + scriptId.utf8());
 
-  std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> matches =
-      searchInTextByLinesImpl(m_session, it->second->source(m_isolate), query,
+  *results = std::make_unique<protocol::Array<protocol::Debugger::SearchMatch>>(
+      searchInTextByLinesImpl(m_session, it->second->source(0), query,
                               optionalCaseSensitive.fromMaybe(false),
-                              optionalIsRegex.fromMaybe(false));
-  *results = protocol::Array<protocol::Debugger::SearchMatch>::create();
-  for (size_t i = 0; i < matches.size(); ++i)
-    (*results)->addItem(std::move(matches[i]));
-  return Response::OK();
+                              optionalIsRegex.fromMaybe(false)));
+  return Response::Success();
 }
 
 Response V8DebuggerAgentImpl::setScriptSource(
     const String16& scriptId, const String16& newContent, Maybe<bool> dryRun,
     Maybe<protocol::Array<protocol::Debugger::CallFrame>>* newCallFrames,
-    Maybe<bool>* stackChanged, Maybe<StackTrace>* asyncStackTrace,
+    Maybe<bool>* stackChanged,
+    Maybe<protocol::Runtime::StackTrace>* asyncStackTrace,
+    Maybe<protocol::Runtime::StackTraceId>* asyncStackTraceId,
     Maybe<protocol::Runtime::ExceptionDetails>* optOutCompileError) {
-  if (!enabled()) return Response::Error(kDebuggerNotEnabled);
+  if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
 
   ScriptsMap::iterator it = m_scripts.find(scriptId);
   if (it == m_scripts.end()) {
-    return Response::Error("No script with given id found");
+    return Response::ServerError("No script with given id found");
   }
-  if (it->second->isModule()) {
-    // TODO(kozyatinskiy): LiveEdit should support ES6 module
-    return Response::Error("Editing module's script is not supported.");
+  int contextId = it->second->executionContextId();
+  InspectedContext* inspected = m_inspector->getContext(contextId);
+  if (!inspected) {
+    return Response::InternalError();
   }
+  v8::HandleScope handleScope(m_isolate);
+  v8::Local<v8::Context> context = inspected->context();
+  v8::Context::Scope contextScope(context);
 
-  v8::HandleScope handles(m_isolate);
-  v8::Local<v8::String> newSource = toV8String(m_isolate, newContent);
-  bool compileError = false;
-  Response response = m_debugger->setScriptSource(
-      scriptId, newSource, dryRun.fromMaybe(false), optOutCompileError,
-      &m_pausedCallFrames, stackChanged, &compileError);
-  if (!response.isSuccess() || compileError) return response;
-
-  it->second->setSource(newSource);
+  v8::debug::LiveEditResult result;
+  it->second->setSource(newContent, dryRun.fromMaybe(false), &result);
+  if (result.status != v8::debug::LiveEditResult::OK) {
+    *optOutCompileError =
+        protocol::Runtime::ExceptionDetails::create()
+            .setExceptionId(m_inspector->nextExceptionId())
+            .setText(toProtocolString(m_isolate, result.message))
+            .setLineNumber(result.line_number != -1 ? result.line_number - 1
+                                                    : 0)
+            .setColumnNumber(result.column_number != -1 ? result.column_number
+                                                        : 0)
+            .build();
+    return Response::Success();
+  } else {
+    *stackChanged = result.stack_changed;
+  }
   std::unique_ptr<Array<CallFrame>> callFrames;
-  response = currentCallFrames(&callFrames);
-  if (!response.isSuccess()) return response;
+  Response response = currentCallFrames(&callFrames);
+  if (!response.IsSuccess()) return response;
   *newCallFrames = std::move(callFrames);
   *asyncStackTrace = currentAsyncStackTrace();
-  return Response::OK();
+  *asyncStackTraceId = currentExternalStackTrace();
+  return Response::Success();
 }
 
 Response V8DebuggerAgentImpl::restartFrame(
     const String16& callFrameId,
     std::unique_ptr<Array<CallFrame>>* newCallFrames,
-    Maybe<StackTrace>* asyncStackTrace) {
-  if (!isPaused()) return Response::Error(kDebuggerNotPaused);
-  InjectedScript::CallFrameScope scope(m_inspector, m_session->contextGroupId(),
-                                       callFrameId);
+    Maybe<protocol::Runtime::StackTrace>* asyncStackTrace,
+    Maybe<protocol::Runtime::StackTraceId>* asyncStackTraceId) {
+  if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
+  InjectedScript::CallFrameScope scope(m_session, callFrameId);
   Response response = scope.initialize();
-  if (!response.isSuccess()) return response;
-  if (scope.frameOrdinal() >= m_pausedCallFrames.size())
-    return Response::Error("Could not find call frame with given id");
-
-  v8::Local<v8::Value> resultValue;
-  v8::Local<v8::Boolean> result;
-  if (!m_pausedCallFrames[scope.frameOrdinal()]->restart().ToLocal(
-          &resultValue) ||
-      scope.tryCatch().HasCaught() ||
-      !resultValue->ToBoolean(scope.context()).ToLocal(&result) ||
-      !result->Value()) {
+  if (!response.IsSuccess()) return response;
+  int frameOrdinal = static_cast<int>(scope.frameOrdinal());
+  auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
+  if (it->Done()) {
+    return Response::ServerError("Could not find call frame with given id");
+  }
+  if (!it->Restart()) {
     return Response::InternalError();
   }
-  JavaScriptCallFrames frames = m_debugger->currentCallFrames();
-  m_pausedCallFrames.swap(frames);
-
   response = currentCallFrames(newCallFrames);
-  if (!response.isSuccess()) return response;
+  if (!response.IsSuccess()) return response;
   *asyncStackTrace = currentAsyncStackTrace();
-  return Response::OK();
+  *asyncStackTraceId = currentExternalStackTrace();
+  return Response::Success();
 }
 
-Response V8DebuggerAgentImpl::getScriptSource(const String16& scriptId,
-                                              String16* scriptSource) {
-  if (!enabled()) return Response::Error(kDebuggerNotEnabled);
+Response V8DebuggerAgentImpl::getScriptSource(
+    const String16& scriptId, String16* scriptSource,
+    Maybe<protocol::Binary>* bytecode) {
+  if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
   ScriptsMap::iterator it = m_scripts.find(scriptId);
   if (it == m_scripts.end())
-    return Response::Error("No script for id: " + scriptId);
-  v8::HandleScope handles(m_isolate);
-  *scriptSource = it->second->source(m_isolate);
-  return Response::OK();
+    return Response::ServerError("No script for id: " + scriptId.utf8());
+  *scriptSource = it->second->source(0);
+  v8::MemorySpan<const uint8_t> span;
+  if (it->second->wasmBytecode().To(&span)) {
+    if (span.size() > kWasmBytecodeMaxLength) {
+      return Response::ServerError(kWasmBytecodeExceedsTransferLimit);
+    }
+    *bytecode = protocol::Binary::fromSpan(span.data(), span.size());
+  }
+  return Response::Success();
+}
+
+Response V8DebuggerAgentImpl::getWasmBytecode(const String16& scriptId,
+                                              protocol::Binary* bytecode) {
+  if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
+  ScriptsMap::iterator it = m_scripts.find(scriptId);
+  if (it == m_scripts.end())
+    return Response::ServerError("No script for id: " + scriptId.utf8());
+  v8::MemorySpan<const uint8_t> span;
+  if (!it->second->wasmBytecode().To(&span))
+    return Response::ServerError("Script with id " + scriptId.utf8() +
+                                 " is not WebAssembly");
+  if (span.size() > kWasmBytecodeMaxLength) {
+    return Response::ServerError(kWasmBytecodeExceedsTransferLimit);
+  }
+  *bytecode = protocol::Binary::fromSpan(span.data(), span.size());
+  return Response::Success();
 }
 
 void V8DebuggerAgentImpl::pushBreakDetails(
@@ -606,78 +1089,93 @@
 void V8DebuggerAgentImpl::schedulePauseOnNextStatement(
     const String16& breakReason,
     std::unique_ptr<protocol::DictionaryValue> data) {
-  if (!enabled() || m_scheduledDebuggerStep == StepInto ||
-      m_javaScriptPauseScheduled || isPaused() ||
-      !m_debugger->breakpointsActivated())
-    return;
-  if (m_breakReason.empty()) m_debugger->setPauseOnNextStatement(true);
+  if (isPaused() || !acceptsPause(false) || !m_breakpointsActive) return;
+  if (m_breakReason.empty()) {
+    m_debugger->setPauseOnNextCall(true, m_session->contextGroupId());
+  }
   pushBreakDetails(breakReason, std::move(data));
 }
 
-void V8DebuggerAgentImpl::schedulePauseOnNextStatementIfSteppingInto() {
-  DCHECK(enabled());
-  if (m_scheduledDebuggerStep != StepInto || m_javaScriptPauseScheduled ||
-      isPaused())
-    return;
-  m_debugger->setPauseOnNextStatement(true);
-}
-
 void V8DebuggerAgentImpl::cancelPauseOnNextStatement() {
-  if (m_javaScriptPauseScheduled || isPaused()) return;
+  if (isPaused() || !acceptsPause(false) || !m_breakpointsActive) return;
+  if (m_breakReason.size() == 1) {
+    m_debugger->setPauseOnNextCall(false, m_session->contextGroupId());
+  }
   popBreakDetails();
-  if (m_breakReason.empty()) m_debugger->setPauseOnNextStatement(false);
 }
 
 Response V8DebuggerAgentImpl::pause() {
-  if (!enabled()) return Response::Error(kDebuggerNotEnabled);
-  if (m_javaScriptPauseScheduled || isPaused()) return Response::OK();
-  clearBreakDetails();
-  m_javaScriptPauseScheduled = true;
-  m_scheduledDebuggerStep = NoStep;
-  m_debugger->setPauseOnNextStatement(true);
-  return Response::OK();
+  if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
+  if (isPaused()) return Response::Success();
+  if (m_debugger->canBreakProgram()) {
+    m_debugger->interruptAndBreak(m_session->contextGroupId());
+  } else {
+    if (m_breakReason.empty()) {
+      m_debugger->setPauseOnNextCall(true, m_session->contextGroupId());
+    }
+    pushBreakDetails(protocol::Debugger::Paused::ReasonEnum::Other, nullptr);
+  }
+  return Response::Success();
 }
 
-Response V8DebuggerAgentImpl::resume() {
-  if (!isPaused()) return Response::Error(kDebuggerNotPaused);
-  m_scheduledDebuggerStep = NoStep;
+Response V8DebuggerAgentImpl::resume(Maybe<bool> terminateOnResume) {
+  if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
   m_session->releaseObjectGroup(kBacktraceObjectGroup);
-  m_debugger->continueProgram();
-  return Response::OK();
+  m_debugger->continueProgram(m_session->contextGroupId(),
+                              terminateOnResume.fromMaybe(false));
+  return Response::Success();
 }
 
-Response V8DebuggerAgentImpl::stepOver() {
-  if (!isPaused()) return Response::Error(kDebuggerNotPaused);
-  // StepOver at function return point should fallback to StepInto.
-  JavaScriptCallFrame* frame =
-      !m_pausedCallFrames.empty() ? m_pausedCallFrames[0].get() : nullptr;
-  if (frame && frame->isAtReturn()) return stepInto();
-  m_scheduledDebuggerStep = StepOver;
+Response V8DebuggerAgentImpl::stepOver(
+    Maybe<protocol::Array<protocol::Debugger::LocationRange>> inSkipList) {
+  if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
+
+  if (inSkipList.isJust()) {
+    const Response res = processSkipList(inSkipList.fromJust());
+    if (res.IsError()) return res;
+  } else {
+    m_skipList.clear();
+  }
+
   m_session->releaseObjectGroup(kBacktraceObjectGroup);
-  m_debugger->stepOverStatement();
-  return Response::OK();
+  m_debugger->stepOverStatement(m_session->contextGroupId());
+  return Response::Success();
 }
 
-Response V8DebuggerAgentImpl::stepInto() {
-  if (!isPaused()) return Response::Error(kDebuggerNotPaused);
-  m_scheduledDebuggerStep = StepInto;
+Response V8DebuggerAgentImpl::stepInto(
+    Maybe<bool> inBreakOnAsyncCall,
+    Maybe<protocol::Array<protocol::Debugger::LocationRange>> inSkipList) {
+  if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
+
+  if (inSkipList.isJust()) {
+    const Response res = processSkipList(inSkipList.fromJust());
+    if (res.IsError()) return res;
+  } else {
+    m_skipList.clear();
+  }
+
   m_session->releaseObjectGroup(kBacktraceObjectGroup);
-  m_debugger->stepIntoStatement();
-  return Response::OK();
+  m_debugger->stepIntoStatement(m_session->contextGroupId(),
+                                inBreakOnAsyncCall.fromMaybe(false));
+  return Response::Success();
 }
 
 Response V8DebuggerAgentImpl::stepOut() {
-  if (!isPaused()) return Response::Error(kDebuggerNotPaused);
-  m_scheduledDebuggerStep = StepOut;
-  m_recursionLevelForStepOut = 1;
+  if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
   m_session->releaseObjectGroup(kBacktraceObjectGroup);
-  m_debugger->stepOutOfFunction();
-  return Response::OK();
+  m_debugger->stepOutOfFunction(m_session->contextGroupId());
+  return Response::Success();
+}
+
+Response V8DebuggerAgentImpl::pauseOnAsyncCall(
+    std::unique_ptr<protocol::Runtime::StackTraceId> inParentStackTraceId) {
+  // Deprecated, just return OK.
+  return Response::Success();
 }
 
 Response V8DebuggerAgentImpl::setPauseOnExceptions(
     const String16& stringPauseState) {
-  if (!enabled()) return Response::Error(kDebuggerNotEnabled);
+  if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
   v8::debug::ExceptionBreakState pauseState;
   if (stringPauseState == "none") {
     pauseState = v8::debug::NoBreakOnException;
@@ -686,14 +1184,16 @@
   } else if (stringPauseState == "uncaught") {
     pauseState = v8::debug::BreakOnUncaughtException;
   } else {
-    return Response::Error("Unknown pause on exceptions mode: " +
-                           stringPauseState);
+    return Response::ServerError("Unknown pause on exceptions mode: " +
+                                 stringPauseState.utf8());
   }
   setPauseOnExceptionsImpl(pauseState);
-  return Response::OK();
+  return Response::Success();
 }
 
 void V8DebuggerAgentImpl::setPauseOnExceptionsImpl(int pauseState) {
+  // TODO(dgozman): this changes the global state and forces all context groups
+  // to pause. We should make this flag be per-context-group.
   m_debugger->setPauseOnExceptionsState(
       static_cast<v8::debug::ExceptionBreakState>(pauseState));
   m_state->setInteger(DebuggerAgentState::pauseOnExceptionsState, pauseState);
@@ -703,98 +1203,195 @@
     const String16& callFrameId, const String16& expression,
     Maybe<String16> objectGroup, Maybe<bool> includeCommandLineAPI,
     Maybe<bool> silent, Maybe<bool> returnByValue, Maybe<bool> generatePreview,
-    Maybe<bool> throwOnSideEffect, std::unique_ptr<RemoteObject>* result,
+    Maybe<bool> throwOnSideEffect, Maybe<double> timeout,
+    std::unique_ptr<RemoteObject>* result,
     Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
-  if (!isPaused()) return Response::Error(kDebuggerNotPaused);
-  InjectedScript::CallFrameScope scope(m_inspector, m_session->contextGroupId(),
-                                       callFrameId);
+  if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
+  InjectedScript::CallFrameScope scope(m_session, callFrameId);
   Response response = scope.initialize();
-  if (!response.isSuccess()) return response;
-  if (scope.frameOrdinal() >= m_pausedCallFrames.size())
-    return Response::Error("Could not find call frame with given id");
-
+  if (!response.IsSuccess()) return response;
   if (includeCommandLineAPI.fromMaybe(false)) scope.installCommandLineAPI();
   if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole();
 
-  v8::MaybeLocal<v8::Value> maybeResultValue =
-      m_pausedCallFrames[scope.frameOrdinal()]->evaluate(
-          toV8String(m_isolate, expression),
-          throwOnSideEffect.fromMaybe(false));
+  int frameOrdinal = static_cast<int>(scope.frameOrdinal());
+  auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
+  if (it->Done()) {
+    return Response::ServerError("Could not find call frame with given id");
+  }
+
+  v8::MaybeLocal<v8::Value> maybeResultValue;
+  {
+    V8InspectorImpl::EvaluateScope evaluateScope(scope);
+    if (timeout.isJust()) {
+      response = evaluateScope.setTimeout(timeout.fromJust() / 1000.0);
+      if (!response.IsSuccess()) return response;
+    }
+    maybeResultValue = it->Evaluate(toV8String(m_isolate, expression),
+                                    throwOnSideEffect.fromMaybe(false));
+  }
+  // Re-initialize after running client's code, as it could have destroyed
+  // context or session.
+  response = scope.initialize();
+  if (!response.IsSuccess()) return response;
+  WrapMode mode = generatePreview.fromMaybe(false) ? WrapMode::kWithPreview
+                                                   : WrapMode::kNoPreview;
+  if (returnByValue.fromMaybe(false)) mode = WrapMode::kForceValue;
+  return scope.injectedScript()->wrapEvaluateResult(
+      maybeResultValue, scope.tryCatch(), objectGroup.fromMaybe(""), mode,
+      result, exceptionDetails);
+}
+
+Response V8DebuggerAgentImpl::executeWasmEvaluator(
+    const String16& callFrameId, const protocol::Binary& evaluator,
+    Maybe<double> timeout,
+    std::unique_ptr<protocol::Runtime::RemoteObject>* result,
+    Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
+  if (!v8::debug::StackTraceIterator::SupportsWasmDebugEvaluate()) {
+    return Response::ServerError(
+        "--wasm-expose-debug-eval is required to execte evaluator modules");
+  }
+  if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
+  InjectedScript::CallFrameScope scope(m_session, callFrameId);
+  Response response = scope.initialize();
+  if (!response.IsSuccess()) return response;
+
+  int frameOrdinal = static_cast<int>(scope.frameOrdinal());
+  std::unique_ptr<v8::debug::StackTraceIterator> it =
+      v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
+  if (it->Done()) {
+    return Response::ServerError("Could not find call frame with given id");
+  }
+  if (!it->GetScript()->IsWasm()) {
+    return Response::ServerError(
+        "executeWasmEvaluator can only be called on WebAssembly frames");
+  }
+
+  v8::MaybeLocal<v8::Value> maybeResultValue;
+  {
+    V8InspectorImpl::EvaluateScope evaluateScope(scope);
+    if (timeout.isJust()) {
+      response = evaluateScope.setTimeout(timeout.fromJust() / 1000.0);
+      if (!response.IsSuccess()) return response;
+    }
+    v8::MaybeLocal<v8::String> eval_result =
+        it->EvaluateWasm({evaluator.data(), evaluator.size()}, frameOrdinal);
+    if (!eval_result.IsEmpty()) maybeResultValue = eval_result.ToLocalChecked();
+  }
 
   // Re-initialize after running client's code, as it could have destroyed
   // context or session.
   response = scope.initialize();
-  if (!response.isSuccess()) return response;
-  return scope.injectedScript()->wrapEvaluateResult(
-      maybeResultValue, scope.tryCatch(), objectGroup.fromMaybe(""),
-      returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), result,
-      exceptionDetails);
+  if (!response.IsSuccess()) return response;
+
+  String16 object_group = "";
+  InjectedScript* injected_script = scope.injectedScript();
+  return injected_script->wrapEvaluateResult(maybeResultValue, scope.tryCatch(),
+                                             object_group, WrapMode::kNoPreview,
+                                             result, exceptionDetails);
 }
 
 Response V8DebuggerAgentImpl::setVariableValue(
     int scopeNumber, const String16& variableName,
     std::unique_ptr<protocol::Runtime::CallArgument> newValueArgument,
     const String16& callFrameId) {
-  if (!enabled()) return Response::Error(kDebuggerNotEnabled);
-  if (!isPaused()) return Response::Error(kDebuggerNotPaused);
-  InjectedScript::CallFrameScope scope(m_inspector, m_session->contextGroupId(),
-                                       callFrameId);
+  if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
+  if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
+  InjectedScript::CallFrameScope scope(m_session, callFrameId);
   Response response = scope.initialize();
-  if (!response.isSuccess()) return response;
+  if (!response.IsSuccess()) return response;
   v8::Local<v8::Value> newValue;
   response = scope.injectedScript()->resolveCallArgument(newValueArgument.get(),
                                                          &newValue);
-  if (!response.isSuccess()) return response;
+  if (!response.IsSuccess()) return response;
 
-  if (scope.frameOrdinal() >= m_pausedCallFrames.size())
-    return Response::Error("Could not find call frame with given id");
-  v8::MaybeLocal<v8::Value> result =
-      m_pausedCallFrames[scope.frameOrdinal()]->setVariableValue(
-          scopeNumber, toV8String(m_isolate, variableName), newValue);
-  if (scope.tryCatch().HasCaught() || result.IsEmpty())
+  int frameOrdinal = static_cast<int>(scope.frameOrdinal());
+  auto it = v8::debug::StackTraceIterator::Create(m_isolate, frameOrdinal);
+  if (it->Done()) {
+    return Response::ServerError("Could not find call frame with given id");
+  }
+  auto scopeIterator = it->GetScopeIterator();
+  while (!scopeIterator->Done() && scopeNumber > 0) {
+    --scopeNumber;
+    scopeIterator->Advance();
+  }
+  if (scopeNumber != 0) {
+    return Response::ServerError("Could not find scope with given number");
+  }
+
+  if (!scopeIterator->SetVariableValue(toV8String(m_isolate, variableName),
+                                       newValue) ||
+      scope.tryCatch().HasCaught()) {
     return Response::InternalError();
-  return Response::OK();
+  }
+  return Response::Success();
+}
+
+Response V8DebuggerAgentImpl::setReturnValue(
+    std::unique_ptr<protocol::Runtime::CallArgument> protocolNewValue) {
+  if (!enabled()) return Response::ServerError(kDebuggerNotEnabled);
+  if (!isPaused()) return Response::ServerError(kDebuggerNotPaused);
+  v8::HandleScope handleScope(m_isolate);
+  auto iterator = v8::debug::StackTraceIterator::Create(m_isolate);
+  if (iterator->Done()) {
+    return Response::ServerError("Could not find top call frame");
+  }
+  if (iterator->GetReturnValue().IsEmpty()) {
+    return Response::ServerError(
+        "Could not update return value at non-return position");
+  }
+  InjectedScript::ContextScope scope(m_session, iterator->GetContextId());
+  Response response = scope.initialize();
+  if (!response.IsSuccess()) return response;
+  v8::Local<v8::Value> newValue;
+  response = scope.injectedScript()->resolveCallArgument(protocolNewValue.get(),
+                                                         &newValue);
+  if (!response.IsSuccess()) return response;
+  v8::debug::SetReturnValue(m_isolate, newValue);
+  return Response::Success();
 }
 
 Response V8DebuggerAgentImpl::setAsyncCallStackDepth(int depth) {
-  if (!enabled()) return Response::Error(kDebuggerNotEnabled);
+  if (!enabled() && !m_session->runtimeAgent()->enabled()) {
+    return Response::ServerError(kDebuggerNotEnabled);
+  }
   m_state->setInteger(DebuggerAgentState::asyncCallStackDepth, depth);
   m_debugger->setAsyncCallStackDepth(this, depth);
-  return Response::OK();
+  return Response::Success();
 }
 
 Response V8DebuggerAgentImpl::setBlackboxPatterns(
     std::unique_ptr<protocol::Array<String16>> patterns) {
-  if (!patterns->length()) {
+  if (patterns->empty()) {
     m_blackboxPattern = nullptr;
     resetBlackboxedStateCache();
     m_state->remove(DebuggerAgentState::blackboxPattern);
-    return Response::OK();
+    return Response::Success();
   }
 
   String16Builder patternBuilder;
   patternBuilder.append('(');
-  for (size_t i = 0; i < patterns->length() - 1; ++i) {
-    patternBuilder.append(patterns->get(i));
+  for (size_t i = 0; i < patterns->size() - 1; ++i) {
+    patternBuilder.append((*patterns)[i]);
     patternBuilder.append("|");
   }
-  patternBuilder.append(patterns->get(patterns->length() - 1));
+  patternBuilder.append(patterns->back());
   patternBuilder.append(')');
   String16 pattern = patternBuilder.toString();
   Response response = setBlackboxPattern(pattern);
-  if (!response.isSuccess()) return response;
+  if (!response.IsSuccess()) return response;
   resetBlackboxedStateCache();
   m_state->setString(DebuggerAgentState::blackboxPattern, pattern);
-  return Response::OK();
+  return Response::Success();
 }
 
 Response V8DebuggerAgentImpl::setBlackboxPattern(const String16& pattern) {
   std::unique_ptr<V8Regex> regex(new V8Regex(
       m_inspector, pattern, true /** caseSensitive */, false /** multiline */));
   if (!regex->isValid())
-    return Response::Error("Pattern parser error: " + regex->errorMessage());
+    return Response::ServerError("Pattern parser error: " +
+                                 regex->errorMessage().utf8());
   m_blackboxPattern = std::move(regex);
-  return Response::OK();
+  return Response::Success();
 }
 
 void V8DebuggerAgentImpl::resetBlackboxedStateCache() {
@@ -809,193 +1406,196 @@
         inPositions) {
   auto it = m_scripts.find(scriptId);
   if (it == m_scripts.end())
-    return Response::Error("No script with passed id.");
+    return Response::ServerError("No script with passed id.");
 
-  if (!inPositions->length()) {
+  if (inPositions->empty()) {
     m_blackboxedPositions.erase(scriptId);
     it->second->resetBlackboxedStateCache();
-    return Response::OK();
+    return Response::Success();
   }
 
   std::vector<std::pair<int, int>> positions;
-  positions.reserve(inPositions->length());
-  for (size_t i = 0; i < inPositions->length(); ++i) {
-    protocol::Debugger::ScriptPosition* position = inPositions->get(i);
-    if (position->getLineNumber() < 0)
-      return Response::Error("Position missing 'line' or 'line' < 0.");
-    if (position->getColumnNumber() < 0)
-      return Response::Error("Position missing 'column' or 'column' < 0.");
+  positions.reserve(inPositions->size());
+  for (const std::unique_ptr<protocol::Debugger::ScriptPosition>& position :
+       *inPositions) {
+    Response res = isValidPosition(position.get());
+    if (res.IsError()) return res;
+
     positions.push_back(
         std::make_pair(position->getLineNumber(), position->getColumnNumber()));
   }
-
-  for (size_t i = 1; i < positions.size(); ++i) {
-    if (positions[i - 1].first < positions[i].first) continue;
-    if (positions[i - 1].first == positions[i].first &&
-        positions[i - 1].second < positions[i].second)
-      continue;
-    return Response::Error(
-        "Input positions array is not sorted or contains duplicate values.");
-  }
+  Response res = isValidRangeOfPositions(positions);
+  if (res.IsError()) return res;
 
   m_blackboxedPositions[scriptId] = positions;
   it->second->resetBlackboxedStateCache();
-  return Response::OK();
-}
-
-void V8DebuggerAgentImpl::willExecuteScript(int scriptId) {
-  changeJavaScriptRecursionLevel(+1);
-  if (m_scheduledDebuggerStep != StepInto) return;
-  schedulePauseOnNextStatementIfSteppingInto();
-}
-
-void V8DebuggerAgentImpl::didExecuteScript() {
-  changeJavaScriptRecursionLevel(-1);
-}
-
-void V8DebuggerAgentImpl::changeJavaScriptRecursionLevel(int step) {
-  if (m_javaScriptPauseScheduled && !m_skipAllPauses && !isPaused()) {
-    // Do not ever loose user's pause request until we have actually paused.
-    m_debugger->setPauseOnNextStatement(true);
-  }
-  if (m_scheduledDebuggerStep == StepOut) {
-    m_recursionLevelForStepOut += step;
-    if (!m_recursionLevelForStepOut) {
-      // When StepOut crosses a task boundary (i.e. js -> c++) from where it was
-      // requested,
-      // switch stepping to step into a next JS task, as if we exited to a
-      // blackboxed framework.
-      m_scheduledDebuggerStep = StepInto;
-    }
-  }
+  return Response::Success();
 }
 
 Response V8DebuggerAgentImpl::currentCallFrames(
     std::unique_ptr<Array<CallFrame>>* result) {
   if (!isPaused()) {
-    *result = Array<CallFrame>::create();
-    return Response::OK();
+    *result = std::make_unique<Array<CallFrame>>();
+    return Response::Success();
   }
   v8::HandleScope handles(m_isolate);
-  v8::Local<v8::Context> debuggerContext =
-      v8::debug::GetDebugContext(m_isolate);
-  v8::Context::Scope contextScope(debuggerContext);
-
-  v8::Local<v8::Array> objects = v8::Array::New(m_isolate);
-
-  for (size_t frameOrdinal = 0; frameOrdinal < m_pausedCallFrames.size();
-       ++frameOrdinal) {
-    const std::unique_ptr<JavaScriptCallFrame>& currentCallFrame =
-        m_pausedCallFrames[frameOrdinal];
-
-    v8::Local<v8::Object> details;
-    if (!currentCallFrame->details().ToLocal(&details))
-      return Response::InternalError();
-
-    int contextId = currentCallFrame->contextId();
-
+  *result = std::make_unique<Array<CallFrame>>();
+  auto iterator = v8::debug::StackTraceIterator::Create(m_isolate);
+  int frameOrdinal = 0;
+  for (; !iterator->Done(); iterator->Advance(), frameOrdinal++) {
+    int contextId = iterator->GetContextId();
     InjectedScript* injectedScript = nullptr;
     if (contextId) m_session->findInjectedScript(contextId, injectedScript);
+    String16 callFrameId = RemoteCallFrameId::serialize(
+        m_inspector->isolateId(), contextId, frameOrdinal);
 
-    String16 callFrameId =
-        RemoteCallFrameId::serialize(contextId, static_cast<int>(frameOrdinal));
-    if (!details
-             ->Set(debuggerContext,
-                   toV8StringInternalized(m_isolate, "callFrameId"),
-                   toV8String(m_isolate, callFrameId))
-             .FromMaybe(false)) {
-      return Response::InternalError();
-    }
+    v8::debug::Location loc = iterator->GetSourceLocation();
 
+    std::unique_ptr<Array<Scope>> scopes;
+    auto scopeIterator = iterator->GetScopeIterator();
+    Response res =
+        buildScopes(m_isolate, scopeIterator.get(), injectedScript, &scopes);
+    if (!res.IsSuccess()) return res;
+
+    std::unique_ptr<RemoteObject> protocolReceiver;
     if (injectedScript) {
-      v8::Local<v8::Value> scopeChain;
-      if (!details
-               ->Get(debuggerContext,
-                     toV8StringInternalized(m_isolate, "scopeChain"))
-               .ToLocal(&scopeChain) ||
-          !scopeChain->IsArray()) {
-        return Response::InternalError();
+      v8::Local<v8::Value> receiver;
+      if (iterator->GetReceiver().ToLocal(&receiver)) {
+        res =
+            injectedScript->wrapObject(receiver, kBacktraceObjectGroup,
+                                       WrapMode::kNoPreview, &protocolReceiver);
+        if (!res.IsSuccess()) return res;
       }
-      v8::Local<v8::Array> scopeChainArray = scopeChain.As<v8::Array>();
-      Response response = injectedScript->wrapPropertyInArray(
-          scopeChainArray, toV8StringInternalized(m_isolate, "object"),
-          kBacktraceObjectGroup);
-      if (!response.isSuccess()) return response;
-      response = injectedScript->wrapObjectProperty(
-          details, toV8StringInternalized(m_isolate, "this"),
-          kBacktraceObjectGroup);
-      if (!response.isSuccess()) return response;
-      if (details
-              ->Has(debuggerContext,
-                    toV8StringInternalized(m_isolate, "returnValue"))
-              .FromMaybe(false)) {
-        response = injectedScript->wrapObjectProperty(
-            details, toV8StringInternalized(m_isolate, "returnValue"),
-            kBacktraceObjectGroup);
-        if (!response.isSuccess()) return response;
-      }
-    } else {
-      if (!details
-               ->Set(debuggerContext,
-                     toV8StringInternalized(m_isolate, "scopeChain"),
-                     v8::Array::New(m_isolate, 0))
-               .FromMaybe(false)) {
-        return Response::InternalError();
-      }
-      v8::Local<v8::Object> remoteObject = v8::Object::New(m_isolate);
-      if (!remoteObject
-               ->Set(debuggerContext, toV8StringInternalized(m_isolate, "type"),
-                     toV8StringInternalized(m_isolate, "undefined"))
-               .FromMaybe(false)) {
-        return Response::InternalError();
-      }
-      if (!details
-               ->Set(debuggerContext, toV8StringInternalized(m_isolate, "this"),
-                     remoteObject)
-               .FromMaybe(false)) {
-        return Response::InternalError();
-      }
-      if (!details
-               ->Delete(debuggerContext,
-                        toV8StringInternalized(m_isolate, "returnValue"))
-               .FromMaybe(false)) {
-        return Response::InternalError();
-      }
+    }
+    if (!protocolReceiver) {
+      protocolReceiver = RemoteObject::create()
+                             .setType(RemoteObject::TypeEnum::Undefined)
+                             .build();
     }
 
-    if (!objects->Set(debuggerContext, static_cast<int>(frameOrdinal), details)
-             .FromMaybe(false)) {
-      return Response::InternalError();
+    v8::Local<v8::debug::Script> script = iterator->GetScript();
+    DCHECK(!script.IsEmpty());
+    std::unique_ptr<protocol::Debugger::Location> location =
+        protocol::Debugger::Location::create()
+            .setScriptId(String16::fromInteger(script->Id()))
+            .setLineNumber(loc.GetLineNumber())
+            .setColumnNumber(loc.GetColumnNumber())
+            .build();
+    String16 scriptId = String16::fromInteger(script->Id());
+    ScriptsMap::iterator scriptIterator =
+        m_scripts.find(location->getScriptId());
+    String16 url;
+    if (scriptIterator != m_scripts.end()) {
+      url = scriptIterator->second->sourceURL();
     }
+
+    auto frame = CallFrame::create()
+                     .setCallFrameId(callFrameId)
+                     .setFunctionName(toProtocolString(
+                         m_isolate, iterator->GetFunctionDebugName()))
+                     .setLocation(std::move(location))
+                     .setUrl(url)
+                     .setScopeChain(std::move(scopes))
+                     .setThis(std::move(protocolReceiver))
+                     .build();
+
+    v8::Local<v8::Function> func = iterator->GetFunction();
+    if (!func.IsEmpty()) {
+      frame->setFunctionLocation(
+          protocol::Debugger::Location::create()
+              .setScriptId(String16::fromInteger(func->ScriptId()))
+              .setLineNumber(func->GetScriptLineNumber())
+              .setColumnNumber(func->GetScriptColumnNumber())
+              .build());
+    }
+
+    v8::Local<v8::Value> returnValue = iterator->GetReturnValue();
+    if (!returnValue.IsEmpty() && injectedScript) {
+      std::unique_ptr<RemoteObject> value;
+      res = injectedScript->wrapObject(returnValue, kBacktraceObjectGroup,
+                                       WrapMode::kNoPreview, &value);
+      if (!res.IsSuccess()) return res;
+      frame->setReturnValue(std::move(value));
+    }
+    (*result)->emplace_back(std::move(frame));
   }
-
-  std::unique_ptr<protocol::Value> protocolValue;
-  Response response = toProtocolValue(debuggerContext, objects, &protocolValue);
-  if (!response.isSuccess()) return response;
-  protocol::ErrorSupport errorSupport;
-  *result = Array<CallFrame>::fromValue(protocolValue.get(), &errorSupport);
-  if (!*result) return Response::Error(errorSupport.errors());
-  TranslateWasmStackTraceLocations(result->get(),
-                                   m_debugger->wasmTranslation());
-  return Response::OK();
+  return Response::Success();
 }
 
-std::unique_ptr<StackTrace> V8DebuggerAgentImpl::currentAsyncStackTrace() {
-  if (!isPaused()) return nullptr;
-  V8StackTraceImpl* stackTrace = m_debugger->currentAsyncCallChain();
-  return stackTrace ? stackTrace->buildInspectorObjectForTail(m_debugger)
-                    : nullptr;
+std::unique_ptr<protocol::Runtime::StackTrace>
+V8DebuggerAgentImpl::currentAsyncStackTrace() {
+  std::shared_ptr<AsyncStackTrace> asyncParent =
+      m_debugger->currentAsyncParent();
+  if (!asyncParent) return nullptr;
+  return asyncParent->buildInspectorObject(
+      m_debugger, m_debugger->maxAsyncCallChainDepth() - 1);
 }
 
-bool V8DebuggerAgentImpl::isPaused() const { return m_debugger->isPaused(); }
+std::unique_ptr<protocol::Runtime::StackTraceId>
+V8DebuggerAgentImpl::currentExternalStackTrace() {
+  V8StackTraceId externalParent = m_debugger->currentExternalParent();
+  if (externalParent.IsInvalid()) return nullptr;
+  return protocol::Runtime::StackTraceId::create()
+      .setId(stackTraceIdToString(externalParent.id))
+      .setDebuggerId(V8DebuggerId(externalParent.debugger_id).toString())
+      .build();
+}
+
+bool V8DebuggerAgentImpl::isPaused() const {
+  return m_debugger->isPausedInContextGroup(m_session->contextGroupId());
+}
+
+static String16 getScriptLanguage(const V8DebuggerScript& script) {
+  switch (script.getLanguage()) {
+    case V8DebuggerScript::Language::WebAssembly:
+      return protocol::Debugger::ScriptLanguageEnum::WebAssembly;
+    case V8DebuggerScript::Language::JavaScript:
+      return protocol::Debugger::ScriptLanguageEnum::JavaScript;
+  }
+}
+
+static const char* getDebugSymbolTypeName(
+    v8::debug::WasmScript::DebugSymbolsType type) {
+  switch (type) {
+    case v8::debug::WasmScript::DebugSymbolsType::None:
+      return v8_inspector::protocol::Debugger::DebugSymbols::TypeEnum::None;
+    case v8::debug::WasmScript::DebugSymbolsType::SourceMap:
+      return v8_inspector::protocol::Debugger::DebugSymbols::TypeEnum::
+          SourceMap;
+    case v8::debug::WasmScript::DebugSymbolsType::EmbeddedDWARF:
+      return v8_inspector::protocol::Debugger::DebugSymbols::TypeEnum::
+          EmbeddedDWARF;
+    case v8::debug::WasmScript::DebugSymbolsType::ExternalDWARF:
+      return v8_inspector::protocol::Debugger::DebugSymbols::TypeEnum::
+          ExternalDWARF;
+  }
+}
+
+static std::unique_ptr<protocol::Debugger::DebugSymbols> getDebugSymbols(
+    const V8DebuggerScript& script) {
+  v8::debug::WasmScript::DebugSymbolsType type;
+  if (!script.getDebugSymbolsType().To(&type)) return {};
+
+  std::unique_ptr<protocol::Debugger::DebugSymbols> debugSymbols =
+      v8_inspector::protocol::Debugger::DebugSymbols::create()
+          .setType(getDebugSymbolTypeName(type))
+          .build();
+  String16 externalUrl;
+  if (script.getExternalDebugSymbolsURL().To(&externalUrl)) {
+    debugSymbols->setExternalURL(externalUrl);
+  }
+  return debugSymbols;
+}
 
 void V8DebuggerAgentImpl::didParseSource(
     std::unique_ptr<V8DebuggerScript> script, bool success) {
   v8::HandleScope handles(m_isolate);
-  String16 scriptSource = script->source(m_isolate);
-  if (!success) script->setSourceURL(findSourceURL(scriptSource, false));
-  if (!success)
+  if (!success) {
+    DCHECK(!script->isSourceLoadedLazily());
+    String16 scriptSource = script->source(0);
+    script->setSourceURL(findSourceURL(scriptSource, false));
     script->setSourceMappingURL(findSourceMapURL(scriptSource, false));
+  }
 
   int contextId = script->executionContextId();
   int contextGroupId = m_inspector->contextGroupId(contextId);
@@ -1005,16 +1605,30 @@
   if (inspected) {
     // Script reused between different groups/sessions can have a stale
     // execution context id.
+    const String16& aux = inspected->auxData();
+    std::vector<uint8_t> cbor;
+    v8_crdtp::json::ConvertJSONToCBOR(
+        v8_crdtp::span<uint16_t>(aux.characters16(), aux.length()), &cbor);
     executionContextAuxData = protocol::DictionaryValue::cast(
-        protocol::StringUtil::parseJSON(inspected->auxData()));
+        protocol::Value::parseBinary(cbor.data(), cbor.size()));
   }
   bool isLiveEdit = script->isLiveEdit();
-  bool hasSourceURL = script->hasSourceURL();
+  bool hasSourceURLComment = script->hasSourceURLComment();
   bool isModule = script->isModule();
   String16 scriptId = script->scriptId();
   String16 scriptURL = script->sourceURL();
+  String16 embedderName = script->embedderName();
+  String16 scriptLanguage = getScriptLanguage(*script);
+  Maybe<int> codeOffset;
+  if (script->getLanguage() == V8DebuggerScript::Language::WebAssembly)
+    codeOffset = script->codeOffset();
+  std::unique_ptr<protocol::Debugger::DebugSymbols> debugSymbols =
+      getDebugSymbols(*script);
 
   m_scripts[scriptId] = std::move(script);
+  // Release the strong reference to get notified when debugger is the only
+  // one that holds the script. Has to be done after script added to m_scripts.
+  m_scripts[scriptId]->MakeWeak();
 
   ScriptsMap::iterator scriptIterator = m_scripts.find(scriptId);
   DCHECK(scriptIterator != m_scripts.end());
@@ -1029,59 +1643,133 @@
   Maybe<protocol::DictionaryValue> executionContextAuxDataParam(
       std::move(executionContextAuxData));
   const bool* isLiveEditParam = isLiveEdit ? &isLiveEdit : nullptr;
-  const bool* hasSourceURLParam = hasSourceURL ? &hasSourceURL : nullptr;
+  const bool* hasSourceURLParam =
+      hasSourceURLComment ? &hasSourceURLComment : nullptr;
   const bool* isModuleParam = isModule ? &isModule : nullptr;
-  if (success)
-    m_frontend.scriptParsed(
-        scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
-        scriptRef->endLine(), scriptRef->endColumn(), contextId,
-        scriptRef->hash(m_isolate), std::move(executionContextAuxDataParam),
-        isLiveEditParam, std::move(sourceMapURLParam), hasSourceURLParam,
-        isModuleParam);
-  else
+  std::unique_ptr<V8StackTraceImpl> stack =
+      V8StackTraceImpl::capture(m_inspector->debugger(), contextGroupId, 1);
+  std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
+      stack && !stack->isEmpty()
+          ? stack->buildInspectorObjectImpl(m_debugger, 0)
+          : nullptr;
+
+  if (!success) {
     m_frontend.scriptFailedToParse(
         scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
         scriptRef->endLine(), scriptRef->endColumn(), contextId,
-        scriptRef->hash(m_isolate), std::move(executionContextAuxDataParam),
-        std::move(sourceMapURLParam), hasSourceURLParam, isModuleParam);
-
-  if (scriptURL.isEmpty() || !success) return;
-
-  protocol::DictionaryValue* breakpointsCookie =
-      m_state->getObject(DebuggerAgentState::javaScriptBreakpoints);
-  if (!breakpointsCookie) return;
-
-  for (size_t i = 0; i < breakpointsCookie->size(); ++i) {
-    auto cookie = breakpointsCookie->at(i);
-    protocol::DictionaryValue* breakpointObject =
-        protocol::DictionaryValue::cast(cookie.second);
-    bool isRegex;
-    breakpointObject->getBoolean(DebuggerAgentState::isRegex, &isRegex);
-    String16 url;
-    breakpointObject->getString(DebuggerAgentState::url, &url);
-    if (!matches(m_inspector, scriptURL, url, isRegex)) continue;
-    ScriptBreakpoint breakpoint;
-    breakpoint.script_id = scriptId;
-    breakpointObject->getInteger(DebuggerAgentState::lineNumber,
-                                 &breakpoint.line_number);
-    breakpointObject->getInteger(DebuggerAgentState::columnNumber,
-                                 &breakpoint.column_number);
-    breakpointObject->getString(DebuggerAgentState::condition,
-                                &breakpoint.condition);
-    std::unique_ptr<protocol::Debugger::Location> location =
-        resolveBreakpoint(cookie.first, breakpoint, UserBreakpointSource);
-    if (location)
-      m_frontend.breakpointResolved(cookie.first, std::move(location));
+        scriptRef->hash(), std::move(executionContextAuxDataParam),
+        std::move(sourceMapURLParam), hasSourceURLParam, isModuleParam,
+        scriptRef->length(), std::move(stackTrace), std::move(codeOffset),
+        std::move(scriptLanguage), embedderName);
+    return;
   }
+
+  if (scriptRef->isSourceLoadedLazily()) {
+    m_frontend.scriptParsed(
+        scriptId, scriptURL, 0, 0, 0, 0, contextId, scriptRef->hash(),
+        std::move(executionContextAuxDataParam), isLiveEditParam,
+        std::move(sourceMapURLParam), hasSourceURLParam, isModuleParam, 0,
+        std::move(stackTrace), std::move(codeOffset), std::move(scriptLanguage),
+        std::move(debugSymbols), embedderName);
+  } else {
+    m_frontend.scriptParsed(
+        scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
+        scriptRef->endLine(), scriptRef->endColumn(), contextId,
+        scriptRef->hash(), std::move(executionContextAuxDataParam),
+        isLiveEditParam, std::move(sourceMapURLParam), hasSourceURLParam,
+        isModuleParam, scriptRef->length(), std::move(stackTrace),
+        std::move(codeOffset), std::move(scriptLanguage),
+        std::move(debugSymbols), embedderName);
+  }
+
+  std::vector<protocol::DictionaryValue*> potentialBreakpoints;
+  if (!scriptURL.isEmpty()) {
+    protocol::DictionaryValue* breakpointsByUrl =
+        m_state->getObject(DebuggerAgentState::breakpointsByUrl);
+    if (breakpointsByUrl) {
+      potentialBreakpoints.push_back(breakpointsByUrl->getObject(scriptURL));
+    }
+    potentialBreakpoints.push_back(
+        m_state->getObject(DebuggerAgentState::breakpointsByRegex));
+  }
+  protocol::DictionaryValue* breakpointsByScriptHash =
+      m_state->getObject(DebuggerAgentState::breakpointsByScriptHash);
+  if (breakpointsByScriptHash) {
+    potentialBreakpoints.push_back(
+        breakpointsByScriptHash->getObject(scriptRef->hash()));
+  }
+  protocol::DictionaryValue* breakpointHints =
+      m_state->getObject(DebuggerAgentState::breakpointHints);
+  for (auto breakpoints : potentialBreakpoints) {
+    if (!breakpoints) continue;
+    for (size_t i = 0; i < breakpoints->size(); ++i) {
+      auto breakpointWithCondition = breakpoints->at(i);
+      String16 breakpointId = breakpointWithCondition.first;
+
+      BreakpointType type;
+      String16 selector;
+      int lineNumber = 0;
+      int columnNumber = 0;
+      parseBreakpointId(breakpointId, &type, &selector, &lineNumber,
+                        &columnNumber);
+
+      if (!matches(m_inspector, *scriptRef, type, selector)) continue;
+      String16 condition;
+      breakpointWithCondition.second->asString(&condition);
+      String16 hint;
+      bool hasHint =
+          breakpointHints && breakpointHints->getString(breakpointId, &hint);
+      if (hasHint) {
+        adjustBreakpointLocation(*scriptRef, hint, &lineNumber, &columnNumber);
+      }
+      std::unique_ptr<protocol::Debugger::Location> location =
+          setBreakpointImpl(breakpointId, scriptId, condition, lineNumber,
+                            columnNumber);
+      if (location)
+        m_frontend.breakpointResolved(breakpointId, std::move(location));
+    }
+  }
+  setScriptInstrumentationBreakpointIfNeeded(scriptRef);
 }
 
-void V8DebuggerAgentImpl::didPause(int contextId,
-                                   v8::Local<v8::Value> exception,
-                                   const std::vector<String16>& hitBreakpoints,
-                                   bool isPromiseRejection, bool isUncaught,
-                                   bool isOOMBreak) {
-  JavaScriptCallFrames frames = m_debugger->currentCallFrames();
-  m_pausedCallFrames.swap(frames);
+void V8DebuggerAgentImpl::setScriptInstrumentationBreakpointIfNeeded(
+    V8DebuggerScript* scriptRef) {
+  protocol::DictionaryValue* breakpoints =
+      m_state->getObject(DebuggerAgentState::instrumentationBreakpoints);
+  if (!breakpoints) return;
+  bool isBlackboxed = isFunctionBlackboxed(
+      scriptRef->scriptId(), v8::debug::Location(0, 0),
+      v8::debug::Location(scriptRef->endLine(), scriptRef->endColumn()));
+  if (isBlackboxed) return;
+
+  String16 sourceMapURL = scriptRef->sourceMappingURL();
+  String16 breakpointId = generateInstrumentationBreakpointId(
+      InstrumentationEnum::BeforeScriptExecution);
+  if (!breakpoints->get(breakpointId)) {
+    if (sourceMapURL.isEmpty()) return;
+    breakpointId = generateInstrumentationBreakpointId(
+        InstrumentationEnum::BeforeScriptWithSourceMapExecution);
+    if (!breakpoints->get(breakpointId)) return;
+  }
+  v8::debug::BreakpointId debuggerBreakpointId;
+  if (!scriptRef->setBreakpointOnRun(&debuggerBreakpointId)) return;
+  std::unique_ptr<protocol::DictionaryValue> data =
+      protocol::DictionaryValue::create();
+  data->setString("url", scriptRef->sourceURL());
+  data->setString("scriptId", scriptRef->scriptId());
+  if (!sourceMapURL.isEmpty()) data->setString("sourceMapURL", sourceMapURL);
+
+  m_breakpointsOnScriptRun[debuggerBreakpointId] = std::move(data);
+  m_debuggerBreakpointIdToBreakpointId[debuggerBreakpointId] = breakpointId;
+  m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
+      debuggerBreakpointId);
+}
+
+void V8DebuggerAgentImpl::didPause(
+    int contextId, v8::Local<v8::Value> exception,
+    const std::vector<v8::debug::BreakpointId>& hitBreakpoints,
+    v8::debug::ExceptionType exceptionType, bool isUncaught, bool isOOMBreak,
+    bool isAssert) {
   v8::HandleScope handles(m_isolate);
 
   std::vector<BreakReason> hitReasons;
@@ -1089,47 +1777,55 @@
   if (isOOMBreak) {
     hitReasons.push_back(
         std::make_pair(protocol::Debugger::Paused::ReasonEnum::OOM, nullptr));
+  } else if (isAssert) {
+    hitReasons.push_back(std::make_pair(
+        protocol::Debugger::Paused::ReasonEnum::Assert, nullptr));
   } else if (!exception.IsEmpty()) {
     InjectedScript* injectedScript = nullptr;
     m_session->findInjectedScript(contextId, injectedScript);
     if (injectedScript) {
       String16 breakReason =
-          isPromiseRejection
+          exceptionType == v8::debug::kPromiseRejection
               ? protocol::Debugger::Paused::ReasonEnum::PromiseRejection
               : protocol::Debugger::Paused::ReasonEnum::Exception;
       std::unique_ptr<protocol::Runtime::RemoteObject> obj;
-      injectedScript->wrapObject(exception, kBacktraceObjectGroup, false, false,
-                                 &obj);
+      injectedScript->wrapObject(exception, kBacktraceObjectGroup,
+                                 WrapMode::kNoPreview, &obj);
       std::unique_ptr<protocol::DictionaryValue> breakAuxData;
       if (obj) {
-        breakAuxData = obj->toValue();
+        std::vector<uint8_t> serialized;
+        obj->AppendSerialized(&serialized);
+        breakAuxData = protocol::DictionaryValue::cast(
+            protocol::Value::parseBinary(serialized.data(), serialized.size()));
         breakAuxData->setBoolean("uncaught", isUncaught);
-      } else {
-        breakAuxData = nullptr;
       }
       hitReasons.push_back(
           std::make_pair(breakReason, std::move(breakAuxData)));
     }
   }
 
-  std::unique_ptr<Array<String16>> hitBreakpointIds = Array<String16>::create();
+  auto hitBreakpointIds = std::make_unique<Array<String16>>();
 
-  bool hasDebugCommandBreakpointReason = false;
-  for (const auto& point : hitBreakpoints) {
-    DebugServerBreakpointToBreakpointIdAndSourceMap::iterator
-        breakpointIterator = m_serverBreakpoints.find(point);
-    if (breakpointIterator != m_serverBreakpoints.end()) {
-      const String16& localId = breakpointIterator->second.first;
-      hitBreakpointIds->addItem(localId);
-
-      BreakpointSource source = breakpointIterator->second.second;
-      if (!hasDebugCommandBreakpointReason &&
-          source == DebugCommandBreakpointSource) {
-        hasDebugCommandBreakpointReason = true;
-        hitReasons.push_back(std::make_pair(
-            protocol::Debugger::Paused::ReasonEnum::DebugCommand, nullptr));
-      }
+  for (const auto& id : hitBreakpoints) {
+    auto it = m_breakpointsOnScriptRun.find(id);
+    if (it != m_breakpointsOnScriptRun.end()) {
+      hitReasons.push_back(std::make_pair(
+          protocol::Debugger::Paused::ReasonEnum::Instrumentation,
+          std::move(it->second)));
+      m_breakpointsOnScriptRun.erase(it);
+      continue;
     }
+    auto breakpointIterator = m_debuggerBreakpointIdToBreakpointId.find(id);
+    if (breakpointIterator == m_debuggerBreakpointIdToBreakpointId.end()) {
+      continue;
+    }
+    const String16& breakpointId = breakpointIterator->second;
+    hitBreakpointIds->emplace_back(breakpointId);
+    BreakpointType type;
+    parseBreakpointId(breakpointId, &type);
+    if (type != BreakpointType::kDebugCommand) continue;
+    hitReasons.push_back(std::make_pair(
+        protocol::Debugger::Paused::ReasonEnum::DebugCommand, nullptr));
   }
 
   for (size_t i = 0; i < m_breakReason.size(); ++i) {
@@ -1160,72 +1856,125 @@
 
   std::unique_ptr<Array<CallFrame>> protocolCallFrames;
   Response response = currentCallFrames(&protocolCallFrames);
-  if (!response.isSuccess()) protocolCallFrames = Array<CallFrame>::create();
+  if (!response.IsSuccess())
+    protocolCallFrames = std::make_unique<Array<CallFrame>>();
+
   m_frontend.paused(std::move(protocolCallFrames), breakReason,
                     std::move(breakAuxData), std::move(hitBreakpointIds),
-                    currentAsyncStackTrace());
-  m_scheduledDebuggerStep = NoStep;
-  m_javaScriptPauseScheduled = false;
-
-  if (!m_continueToLocationBreakpointId.isEmpty()) {
-    m_debugger->removeBreakpoint(m_continueToLocationBreakpointId);
-    m_continueToLocationBreakpointId = "";
-  }
+                    currentAsyncStackTrace(), currentExternalStackTrace());
 }
 
 void V8DebuggerAgentImpl::didContinue() {
-  JavaScriptCallFrames emptyCallFrames;
-  m_pausedCallFrames.swap(emptyCallFrames);
   clearBreakDetails();
   m_frontend.resumed();
+  m_frontend.flush();
 }
 
 void V8DebuggerAgentImpl::breakProgram(
     const String16& breakReason,
     std::unique_ptr<protocol::DictionaryValue> data) {
-  if (!enabled() || !m_debugger->canBreakProgram() || m_skipAllPauses) return;
+  if (!enabled() || m_skipAllPauses || !m_debugger->canBreakProgram()) return;
   std::vector<BreakReason> currentScheduledReason;
   currentScheduledReason.swap(m_breakReason);
   pushBreakDetails(breakReason, std::move(data));
-  m_scheduledDebuggerStep = NoStep;
-  m_debugger->breakProgram();
+
+  int contextGroupId = m_session->contextGroupId();
+  int sessionId = m_session->sessionId();
+  V8InspectorImpl* inspector = m_inspector;
+  m_debugger->breakProgram(contextGroupId);
+  // Check that session and |this| are still around.
+  if (!inspector->sessionById(contextGroupId, sessionId)) return;
+  if (!enabled()) return;
+
   popBreakDetails();
   m_breakReason.swap(currentScheduledReason);
+  if (!m_breakReason.empty()) {
+    m_debugger->setPauseOnNextCall(true, m_session->contextGroupId());
+  }
 }
 
-void V8DebuggerAgentImpl::breakProgramOnException(
-    const String16& breakReason,
-    std::unique_ptr<protocol::DictionaryValue> data) {
-  if (!enabled() ||
-      m_debugger->getPauseOnExceptionsState() == v8::debug::NoBreakOnException)
+void V8DebuggerAgentImpl::setBreakpointFor(v8::Local<v8::Function> function,
+                                           v8::Local<v8::String> condition,
+                                           BreakpointSource source) {
+  String16 breakpointId = generateBreakpointId(
+      source == DebugCommandBreakpointSource ? BreakpointType::kDebugCommand
+                                             : BreakpointType::kMonitorCommand,
+      function);
+  if (m_breakpointIdToDebuggerBreakpointIds.find(breakpointId) !=
+      m_breakpointIdToDebuggerBreakpointIds.end()) {
     return;
-  breakProgram(breakReason, std::move(data));
+  }
+  setBreakpointImpl(breakpointId, function, condition);
 }
 
-void V8DebuggerAgentImpl::setBreakpointAt(const String16& scriptId,
-                                          int lineNumber, int columnNumber,
-                                          BreakpointSource source,
-                                          const String16& condition) {
-  ScriptBreakpoint breakpoint(scriptId, lineNumber, columnNumber, condition);
-  String16 breakpointId = generateBreakpointId(breakpoint, source);
-  resolveBreakpoint(breakpointId, breakpoint, source);
-}
-
-void V8DebuggerAgentImpl::removeBreakpointAt(const String16& scriptId,
-                                             int lineNumber, int columnNumber,
-                                             BreakpointSource source) {
-  removeBreakpointImpl(generateBreakpointId(
-      ScriptBreakpoint(scriptId, lineNumber, columnNumber, String16()),
-      source));
+void V8DebuggerAgentImpl::removeBreakpointFor(v8::Local<v8::Function> function,
+                                              BreakpointSource source) {
+  String16 breakpointId = generateBreakpointId(
+      source == DebugCommandBreakpointSource ? BreakpointType::kDebugCommand
+                                             : BreakpointType::kMonitorCommand,
+      function);
+  std::vector<V8DebuggerScript*> scripts;
+  removeBreakpointImpl(breakpointId, scripts);
 }
 
 void V8DebuggerAgentImpl::reset() {
   if (!enabled()) return;
-  m_scheduledDebuggerStep = NoStep;
   m_blackboxedPositions.clear();
   resetBlackboxedStateCache();
+  m_skipList.clear();
   m_scripts.clear();
-  m_breakpointIdToDebuggerBreakpointIds.clear();
+  m_cachedScriptIds.clear();
+  m_cachedScriptSize = 0;
 }
 
+void V8DebuggerAgentImpl::ScriptCollected(const V8DebuggerScript* script) {
+  DCHECK_NE(m_scripts.find(script->scriptId()), m_scripts.end());
+  m_cachedScriptIds.push_back(script->scriptId());
+  // TODO(alph): Properly calculate size when sources are one-byte strings.
+  m_cachedScriptSize += script->length() * sizeof(uint16_t);
+
+  while (m_cachedScriptSize > m_maxScriptCacheSize) {
+    const String16& scriptId = m_cachedScriptIds.front();
+    size_t scriptSize = m_scripts[scriptId]->length() * sizeof(uint16_t);
+    DCHECK_GE(m_cachedScriptSize, scriptSize);
+    m_cachedScriptSize -= scriptSize;
+    m_scripts.erase(scriptId);
+    m_cachedScriptIds.pop_front();
+  }
+}
+
+Response V8DebuggerAgentImpl::processSkipList(
+    protocol::Array<protocol::Debugger::LocationRange>* skipList) {
+  std::unordered_map<String16, std::vector<std::pair<int, int>>> skipListInit;
+  for (std::unique_ptr<protocol::Debugger::LocationRange>& range : *skipList) {
+    protocol::Debugger::ScriptPosition* start = range->getStart();
+    protocol::Debugger::ScriptPosition* end = range->getEnd();
+    String16 scriptId = range->getScriptId();
+
+    auto it = m_scripts.find(scriptId);
+    if (it == m_scripts.end())
+      return Response::ServerError("No script with passed id.");
+
+    Response res = isValidPosition(start);
+    if (res.IsError()) return res;
+
+    res = isValidPosition(end);
+    if (res.IsError()) return res;
+
+    skipListInit[scriptId].emplace_back(start->getLineNumber(),
+                                        start->getColumnNumber());
+    skipListInit[scriptId].emplace_back(end->getLineNumber(),
+                                        end->getColumnNumber());
+  }
+
+  // Verify that the skipList is sorted, and that all ranges
+  // are properly defined (start comes before end).
+  for (auto skipListPair : skipListInit) {
+    Response res = isValidRangeOfPositions(skipListPair.second);
+    if (res.IsError()) return res;
+  }
+
+  m_skipList = std::move(skipListInit);
+  return Response::Success();
+}
 }  // namespace v8_inspector