///|
fn BidiProtocol::handle_browsing_context_navigate_with_state(
  self : BidiProtocol,
  request_id : Int,
  params : Json?,
) -> Unit {
  let ctx_id = match get_param_raw(params, "context") {
    Some(String(value)) => value
    Some(_) => {
      self.send_error(
        request_id, "invalid argument", "context must be a string",
      )
      return
    }
    None => {
      self.send_error(request_id, "invalid argument", "Missing context")
      return
    }
  }
  let url = match get_param_raw(params, "url") {
    Some(String(value)) => value
    Some(_) => {
      self.send_error(request_id, "invalid argument", "url must be a string")
      return
    }
    None => {
      self.send_error(request_id, "invalid argument", "Missing url")
      return
    }
  }
  let wait_mode = match get_param_raw(params, "wait") {
    Some(String(value)) =>
      if value == "none" || value == "interactive" || value == "complete" {
        value
      } else {
        self.send_error(
          request_id, "invalid argument", "wait must be one of none, interactive, or complete",
        )
        return
      }
    Some(_) => {
      self.send_error(request_id, "invalid argument", "wait must be a string")
      return
    }
    None => "none"
  }
  if !self.manager.has_session(ctx_id) {
    self.send_error(request_id, "no such frame", "Unknown context: " + ctx_id)
    return
  }

  match self.prepare_navigation_request_internal(request_id, ctx_id, url) {
    Some(prepared) =>
      match get_field(prepared, "blocked") {
        Some(True) => return
        _ => ()
      }
    None => return
  }

  let resolved_url = self.resolve_effective_navigation_target_url(ctx_id, url)
  self.input_synthetic_events_by_context.remove(ctx_id)
  self.context_synthetic_scrolled.remove(ctx_id)
  self.clear_synthetic_location_href(ctx_id)
  let _ = self.fail_in_flight_navigation(
    ctx_id, "aborted", "Navigation interrupted by another navigation",
  )
  if !is_valid_navigation_target_url(resolved_url) {
    self.send_error(request_id, "invalid argument", "url is invalid")
    return
  }
  if self.navigation_blocked_by_network_conditions(ctx_id, resolved_url) {
    let nav_id = self.next_navigation_id.to_string()
    self.next_navigation_id += 1
    self.emit_navigation_started_event(ctx_id, resolved_url, nav_id)
    self.send_error(
      request_id, "unknown error", "Navigation failed: network offline",
    )
    return
  }
  if should_fail_navigation_with_unknown_error(resolved_url) {
    let nav_id = self.next_navigation_id.to_string()
    self.next_navigation_id += 1
    self.emit_navigation_started_event(ctx_id, resolved_url, nav_id)
    self.send_error(
      request_id, "unknown error", "Navigation failed: navigation target is unreachable",
    )
    return
  }
  if self.is_iframe_navigation_blocked(ctx_id, resolved_url) {
    let nav_id = self.next_navigation_id.to_string()
    self.next_navigation_id += 1
    self.emit_navigation_started_event(ctx_id, resolved_url, nav_id)
    self.emit_navigation_failed_event(ctx_id, resolved_url, nav_id)
    self.send_error(
      request_id, "unknown error", "Navigation failed: blocked by embedding policy",
    )
    return
  }
  match self.manager.get_session(ctx_id) {
    Some(session) =>
      if self.has_beforeunload_prompt_in_scope(ctx_id) {
        let handler = self.resolve_unhandled_prompt_handler(
          ctx_id, "beforeunload",
        )
        let nav_id = self.next_navigation_id.to_string()
        self.next_navigation_id += 1
        self.clear_pending_prompt_state(ctx_id)
        self.pending_prompt_type[ctx_id] = "beforeunload"
        self.pending_beforeunload_request[ctx_id] = request_id
        self.pending_beforeunload_url[ctx_id] = resolved_url
        self.pending_beforeunload_requested_url[ctx_id] = url
        self.pending_beforeunload_wait[ctx_id] = wait_mode
        self.pending_beforeunload_navigation[ctx_id] = nav_id
        self.pending_beforeunload_finalize_mode[ctx_id] = "navigateWithState"
        self.emit_navigation_started_event(ctx_id, resolved_url, nav_id)
        self.emit_user_prompt_opened(
          ctx_id,
          "beforeunload",
          handler,
          "beforeunload",
          None,
        )
        return
      } else {
        let previous_url = session.get_url()
        match session.navigate_to(resolved_url) {
          Ok(_) =>
            self.complete_navigate_with_state(
              request_id,
              ctx_id,
              url,
              resolved_url,
              previous_url,
              wait_mode,
              None,
            )
          Err(e) => {
            let nav_id = self.next_navigation_id.to_string()
            self.next_navigation_id += 1
            self.emit_navigation_started_event(ctx_id, resolved_url, nav_id)
            self.send_error(
              request_id,
              "unknown error",
              "Navigation failed: " + e.message,
            )
          }
        }
      }
    None => {
      self.send_error(request_id, "no such frame", "Unknown context: " + ctx_id)
      return
    }
  }
}

///|
fn BidiProtocol::handle_browsing_context_finalize_reload(
  self : BidiProtocol,
  request_id : Int,
  params : Json?,
) -> Unit {
  let map = match params {
    Some(Object(map)) => map
    _ => {
      self.send_error(
        request_id, "invalid argument", "params must be an object",
      )
      return
    }
  }
  let context_id = match map.get("context") {
    Some(String(context_id)) => context_id
    _ => {
      self.send_error(
        request_id, "invalid argument", "context must be a string",
      )
      return
    }
  }
  if !self.manager.has_session(context_id) {
    self.send_error(
      request_id,
      "no such frame",
      "Unknown context: " + context_id,
    )
    return
  }
  let navigation_id : String? = match map.get("navigation") {
    Some(String(navigation_id)) => Some(navigation_id)
    Some(Null) | None => None
    Some(_) => {
      self.send_error(
        request_id, "invalid argument", "navigation must be a string",
      )
      return
    }
  }
  let url = self.requested_navigation_url(context_id)
  let emitted = self.emit_synthetic_navigation_request_sequence(
    context_id, url, navigation_id,
  )
  self.send_success(
    request_id,
    Some(
      make_object({
        "url": Json::string(url),
        "emitted": Json::number(emitted.to_double()),
        "blocked": Json::boolean(
          self.has_network_blocked_navigation_request(context_id, navigation_id),
        ),
      }),
    ),
  )
}

///|
fn BidiProtocol::handle_browsing_context_reload_with_state(
  self : BidiProtocol,
  request_id : Int,
  params : Json?,
) -> Unit {
  let ctx_id = match get_param_raw(params, "context") {
    Some(String(value)) => value
    Some(_) => {
      self.send_error(
        request_id, "invalid argument", "context must be a string",
      )
      return
    }
    None => {
      self.send_error(request_id, "invalid argument", "Missing context")
      return
    }
  }
  let ignore_cache_raw = match get_param_raw(params, "ignoreCache") {
    Some(value) => Some(value)
    None => get_param_raw(params, "ignore_cache")
  }
  let _ignore_cache = match ignore_cache_raw {
    Some(True) => true
    Some(False) => false
    Some(_) => {
      self.send_error(
        request_id, "invalid argument", "ignoreCache must be a boolean",
      )
      return
    }
    None => false
  }
  let wait_mode = match get_param_raw(params, "wait") {
    Some(String(value)) =>
      if value == "none" || value == "interactive" || value == "complete" {
        value
      } else {
        self.send_error(
          request_id, "invalid argument", "wait must be one of none, interactive, or complete",
        )
        return
      }
    Some(_) => {
      self.send_error(request_id, "invalid argument", "wait must be a string")
      return
    }
    None => "none"
  }
  if !self.manager.has_session(ctx_id) {
    self.send_error(request_id, "no such frame", "Unknown context: " + ctx_id)
    return
  }
  self.input_synthetic_events_by_context.remove(ctx_id)
  self.context_synthetic_scrolled.remove(ctx_id)
  self.clear_synthetic_location_href(ctx_id)
  let _ = self.fail_in_flight_navigation(
    ctx_id, "aborted", "Navigation interrupted by reload",
  )
  match self.manager.get_session(ctx_id) {
    Some(session) => {
      let current_url = session.get_url()
      let requested_url = self.requested_navigation_url(ctx_id)
      let url = if requested_url != "" { requested_url } else { current_url }
      if self.has_beforeunload_prompt_in_scope(ctx_id) {
        let handler = self.resolve_unhandled_prompt_handler(
          ctx_id, "beforeunload",
        )
        let nav_id = self.next_navigation_id.to_string()
        self.next_navigation_id += 1
        self.clear_pending_prompt_state(ctx_id)
        self.pending_prompt_type[ctx_id] = "beforeunload"
        self.pending_beforeunload_request[ctx_id] = request_id
        self.pending_beforeunload_url[ctx_id] = url
        self.pending_beforeunload_requested_url[ctx_id] = url
        self.pending_beforeunload_wait[ctx_id] = wait_mode
        self.pending_beforeunload_navigation[ctx_id] = nav_id
        self.pending_beforeunload_finalize_mode[ctx_id] = "reloadWithState"
        self.emit_navigation_started_event(ctx_id, url, nav_id)
        self.emit_user_prompt_opened(
          ctx_id,
          "beforeunload",
          handler,
          "beforeunload",
          None,
        )
        return
      }
      let completion = self.build_navigation_completion(
        request_id,
        ctx_id,
        url,
        wait_mode,
        None,
      )
      let _ = self.emit_synthetic_navigation_request_sequence(
        ctx_id,
        requested_url,
        Some(completion.navigation_id),
      )
      let blocked = self.has_network_blocked_navigation_request(
        ctx_id,
        Some(completion.navigation_id),
      )
      if blocked {
        self.strip_pending_navigation_lifecycle_events(
          ctx_id,
          url,
          completion.navigation_id,
        )
        self.register_in_flight_navigation(
          ctx_id,
          url,
          completion.navigation_id,
          Some(request_id),
        )
        return
      }
      if !completion.deferred_response {
        self.send_success(request_id, Some(completion.result))
      }
    }
    None => {
      self.send_error(request_id, "no such frame", "Unknown context: " + ctx_id)
      return
    }
  }
}