///|
/// WebDriver BiDi Protocol (minimal)
///
/// Focuses on a BiDi-first automation surface while reusing CDP sessions.
/// This is intentionally small and grows as coverage expands.

///|
/// Stored script.addPreloadScript registration.
priv struct PreloadScriptEntry {
  script_id : String
  function_declaration : String
  arguments : Array[Json]
  sandbox : String?
  contexts : Array[String]
  user_contexts : Array[String]
}

///|
/// Parsed script.addPreloadScript parameters.
priv struct AddPreloadScriptParams {
  function_declaration : String
  arguments : Array[Json]
  sandbox : String?
  contexts : Array[String]
  user_contexts : Array[String]
}

///|
priv struct NavigationCompletion {
  result : Json
  navigation_id : String
  deferred_response : Bool
}

///|
/// BiDi protocol handler
priv struct BidiProtocol {
  manager : CdpSessionManager
  mut outbox : Array[BidiOutMessage]
  mut default_context_id : String?
  mut active_context_id : String?
  mut next_navigation_id : Int
  mut next_realm_id : Int
  mut next_preload_script_id : Int
  subscription_state : BidiSubscriptionState
  user_contexts : Map[String, Bool]
  user_context_prompt_behavior : Map[String, Json] // user context -> unhandledPromptBehavior object
  mut global_download_behavior : Json?
  user_context_download_behavior : Map[String, Json] // user context -> downloadBehavior object
  installed_web_extensions : Map[String, String] // extension id -> source kind
  mut next_web_extension_id : Int
  permission_state_by_scope : Map[String, String] // "|||" -> state
  emulation_state : BidiEmulationState
  geolocation_watch_channel_by_scope : Map[String, String] // "|" -> script.message channel
  geolocation_watch_realm_by_scope : Map[String, String] // "|" -> originating realm
  geolocation_watch_notify_success_by_scope : Map[String, Bool] // "|" -> emit coords updates
  geolocation_watch_notify_error_by_scope : Map[String, Bool] // "|" -> emit error updates
  mut next_geolocation_watch_id : Int
  bluetooth_adapter_state_by_context : Map[String, String] // context -> absent|powered-off|powered-on
  bluetooth_simulation_disabled_by_context : Map[String, Bool] // context -> simulation disabled
  bluetooth_preconnected_devices_by_context : Map[String, Array[Json]] // context -> synthetic device descriptors
  bluetooth_granted_devices_by_context : Map[String, Array[Json]] // context -> granted synthetic device descriptors
  bluetooth_pending_prompt_request : Map[String, Int] // context -> deferred requestDevice request id
  bluetooth_pending_prompt_realm : Map[String, String] // context -> deferred requestDevice realm id
  bluetooth_pending_prompt_unwrap_result : Map[String, Bool] // context -> deferred requestDevice unwrap mode
  bluetooth_pending_prompt_id : Map[String, String] // context -> active bluetooth prompt id
  bluetooth_pending_prompt_device_address : Map[String, String] // context -> prompt-selected device address
  bluetooth_pending_prompt_device_name : Map[String, String] // context -> prompt-selected device name
  bluetooth_gatt_connected_by_scope : Map[String, Bool] // "|
" -> connected flag bluetooth_services_by_scope : Map[String, Array[String]] // "|
" -> primary service uuids bluetooth_characteristics_by_scope : Map[String, Json] // "|
||" -> properties object bluetooth_descriptors_by_scope : Map[String, Bool] // "|
|||" -> descriptor exists bluetooth_pending_gatt_request : Map[String, Int] // context -> deferred gatt.connect request id bluetooth_pending_gatt_realm : Map[String, String] // context -> deferred gatt.connect realm id bluetooth_pending_gatt_unwrap_result : Map[String, Bool] // context -> deferred gatt.connect unwrap mode bluetooth_pending_gatt_address : Map[String, String] // context -> deferred gatt.connect device address bluetooth_pending_gatt_response_kind : Map[String, String] // context -> deferred response mode bluetooth_pending_characteristic_request : Map[String, Int] // context -> deferred characteristic request id bluetooth_pending_characteristic_realm : Map[String, String] // context -> deferred characteristic realm id bluetooth_pending_characteristic_unwrap_result : Map[String, Bool] // context -> deferred characteristic unwrap mode bluetooth_pending_characteristic_address : Map[String, String] // context -> deferred characteristic device address bluetooth_pending_characteristic_service_uuid : Map[String, String] // context -> deferred characteristic service uuid bluetooth_pending_characteristic_uuid : Map[String, String] // context -> deferred characteristic uuid bluetooth_pending_characteristic_type : Map[String, String] // context -> deferred characteristic response type bluetooth_pending_descriptor_request : Map[String, Int] // context -> deferred descriptor request id bluetooth_pending_descriptor_realm : Map[String, String] // context -> deferred descriptor realm id bluetooth_pending_descriptor_unwrap_result : Map[String, Bool] // context -> deferred descriptor unwrap mode bluetooth_pending_descriptor_address : Map[String, String] // context -> deferred descriptor device address bluetooth_pending_descriptor_service_uuid : Map[String, String] // context -> deferred descriptor service uuid bluetooth_pending_descriptor_characteristic_uuid : Map[String, String] // context -> deferred descriptor characteristic uuid bluetooth_pending_descriptor_uuid : Map[String, String] // context -> deferred descriptor uuid bluetooth_pending_descriptor_type : Map[String, String] // context -> deferred descriptor response type mut next_bluetooth_prompt_id : Int user_context_viewport_width : Map[String, Int] // user context -> default viewport width override user_context_viewport_height : Map[String, Int] // user context -> default viewport height override user_context_device_pixel_ratio : Map[String, Double] // user context -> default devicePixelRatio override context_user_context : Map[String, String] context_has_beforeunload : Map[String, Bool] context_blocks_cross_origin_iframe_navigation : Map[String, Bool] // context -> active CSP/COEP frame policy pending_prompt_type : Map[String, String] // context -> prompt type pending_close_request : Map[String, Int] // context -> browsingContext.close request id pending_close_wait_for_destroyed : Map[String, Bool] // context -> closeWithState wait flag pending_close_response_mode : Map[String, String] // context -> close response kind pending_prompt_request : Map[String, Int] // context -> deferred script.evaluate request id pending_prompt_realm : Map[String, String] // context -> deferred script.evaluate realm id pending_prompt_unwrap_result : Map[String, Bool] // context -> deferred script.evaluate unwrap mode pending_beforeunload_request : Map[String, Int] // context -> deferred navigate request id pending_beforeunload_url : Map[String, String] // context -> deferred navigate destination pending_beforeunload_requested_url : Map[String, String] // context -> deferred original requested URL pending_beforeunload_wait : Map[String, String] // context -> deferred navigate wait mode pending_beforeunload_navigation : Map[String, String] // context -> deferred navigation id pending_beforeunload_finalize_mode : Map[String, String] // context -> deferred internal navigate/reload mode pending_navigation_request : Map[String, Int] // context -> deferred navigate/reload request id pending_navigation_response_mode : Map[String, String] // context -> deferred navigation response kind in_flight_navigation_url : Map[String, String] // context -> interruptible navigation URL in_flight_navigation_id : Map[String, String] // context -> interruptible navigation id in_flight_navigation_started_at : Map[String, Double] // context -> interruptible navigation start timestamp pending_prompt_result_var : Map[String, String] // context -> window property to store prompt result context_local_storage : Map[String, Map[String, String]] // context -> localStorage key/value storage_cookies_by_scope : Map[String, Map[String, StoredCookie]] // scope -> stored cookies synthetic_location_href_by_context : Map[String, String] // context -> synthetic window.location.href override context_last_started_navigation : Map[String, String] // context -> last emitted navigationStarted id context_requested_navigation_url : Map[String, String] // context -> last requested navigation URL context_original_opener : Map[String, String] context_realm : Map[String, String] context_sandbox_realm : Map[String, String] // "::" -> realm realm_context : Map[String, String] // realm -> context context_worker_realms : Map[String, Array[String]] // context -> worker realms realm_sandbox : Map[String, String] // sandbox realm -> sandbox name context_parent : Map[String, String] context_children : Map[String, Array[String]] context_had_synthetic_child : Map[String, Bool] // top-level context -> has synthetic child helper context_client_window : Map[String, String] context_viewport_width : Map[String, Int] // context -> viewport width override context_viewport_height : Map[String, Int] // context -> viewport height override context_device_pixel_ratio : Map[String, Double] // context -> devicePixelRatio override input_pressed_keys : Map[String, Array[String]] // context -> pressed key stack input_pointer_x : Map[String, Double] // context -> current pointer x input_pointer_y : Map[String, Double] // context -> current pointer y input_pointer_buttons : Map[String, Int] // context -> mouse buttons bitfield input_pointer_type : Map[String, String] // context -> last pointer type input_pointer_target_shared : Map[String, String] // context -> last element-origin sharedId input_pointer_down_x : Map[String, Double] // context -> primary pointerDown x input_pointer_down_y : Map[String, Double] // context -> primary pointerDown y input_pointer_down_target_shared : Map[String, String] // context -> primary pointerDown target input_pointer_dragged : Map[String, Bool] // context -> whether pointer moved while primary button pressed input_html_drag_source_shared : Map[String, String] // context -> active HTML draggable source sharedId input_html_drag_active : Map[String, Bool] // context -> whether dragstart has been emitted input_html_drag_over_shared : Map[String, String] // context -> current dragover target sharedId input_html_drag_accept_shared : Map[String, String] // context -> dragover target that accepted drop input_html_drag_transfer_id : Map[String, String] // context -> shared synthetic DataTransfer key input_last_click_time : Map[String, Double] // context -> timestamp of last primary click release input_last_click_x : Map[String, Double] // context -> x of last primary click release input_last_click_y : Map[String, Double] // context -> y of last primary click release input_last_click_target_shared : Map[String, String] // context -> target of last primary click release input_pending_double_click : Map[String, Bool] // context -> whether current click sequence should emit dblclick input_action_clock_ms : Map[String, Double] // context -> virtual input clock advanced by pause durations input_synthetic_events_by_context : Map[String, Array[Json]] // context -> synthetic window.allEvents.events entries context_synthetic_scrolled : Map[String, Bool] // context -> synthetic scroll fallback applied speculation_prefetch_target_by_context : Map[String, String] // context -> last synthetic prefetch target network_state : @crater_network.BidiNetworkState mut socket : @core.Any? // WebSocket for async responses mut socket_epoch : Int // Monotonic epoch for active WebSocket use_quickjs : Bool // Use QuickJS sandboxed runtime for script execution mut dom_initialized : Bool // Deno-side mock DOM initialized mut preload_scripts : Array[PreloadScriptEntry] } ///| fn[T] BidiProtocol::resolve_effective_scoped_override( self : BidiProtocol, ctx_id : String, by_context : Map[String, T], by_user_context : Map[String, T], global : T?, ) -> T? { let ancestry = self.storage_context_ancestry(ctx_id) let mut index = ancestry.length() while index > 0 { index = index - 1 let scope_id = ancestry[index] match by_context.get(scope_id) { Some(override_value) => return Some(override_value) None => () } } let user_context_id = self.context_user_context .get(ctx_id) .unwrap_or("default") match by_user_context.get(user_context_id) { Some(override_value) => Some(override_value) None => global } } ///| /// Create new BiDi protocol instance fn BidiProtocol::new() -> BidiProtocol { let user_contexts : Map[String, Bool] = {} user_contexts["default"] = true { manager: CdpSessionManager::new(), outbox: [], default_context_id: None, active_context_id: None, next_navigation_id: 1, next_realm_id: 1, next_preload_script_id: 1, subscription_state: BidiSubscriptionState::new(), user_contexts, user_context_prompt_behavior: {}, global_download_behavior: None, user_context_download_behavior: {}, installed_web_extensions: {}, next_web_extension_id: 1, permission_state_by_scope: {}, emulation_state: BidiEmulationState::new(), geolocation_watch_channel_by_scope: {}, geolocation_watch_realm_by_scope: {}, geolocation_watch_notify_success_by_scope: {}, geolocation_watch_notify_error_by_scope: {}, next_geolocation_watch_id: 1, bluetooth_adapter_state_by_context: {}, bluetooth_simulation_disabled_by_context: {}, bluetooth_preconnected_devices_by_context: {}, bluetooth_granted_devices_by_context: {}, bluetooth_pending_prompt_request: {}, bluetooth_pending_prompt_realm: {}, bluetooth_pending_prompt_unwrap_result: {}, bluetooth_pending_prompt_id: {}, bluetooth_pending_prompt_device_address: {}, bluetooth_pending_prompt_device_name: {}, bluetooth_gatt_connected_by_scope: {}, bluetooth_services_by_scope: {}, bluetooth_characteristics_by_scope: {}, bluetooth_descriptors_by_scope: {}, bluetooth_pending_gatt_request: {}, bluetooth_pending_gatt_realm: {}, bluetooth_pending_gatt_unwrap_result: {}, bluetooth_pending_gatt_address: {}, bluetooth_pending_gatt_response_kind: {}, bluetooth_pending_characteristic_request: {}, bluetooth_pending_characteristic_realm: {}, bluetooth_pending_characteristic_unwrap_result: {}, bluetooth_pending_characteristic_address: {}, bluetooth_pending_characteristic_service_uuid: {}, bluetooth_pending_characteristic_uuid: {}, bluetooth_pending_characteristic_type: {}, bluetooth_pending_descriptor_request: {}, bluetooth_pending_descriptor_realm: {}, bluetooth_pending_descriptor_unwrap_result: {}, bluetooth_pending_descriptor_address: {}, bluetooth_pending_descriptor_service_uuid: {}, bluetooth_pending_descriptor_characteristic_uuid: {}, bluetooth_pending_descriptor_uuid: {}, bluetooth_pending_descriptor_type: {}, next_bluetooth_prompt_id: 1, user_context_viewport_width: {}, user_context_viewport_height: {}, user_context_device_pixel_ratio: {}, context_user_context: {}, context_has_beforeunload: {}, context_blocks_cross_origin_iframe_navigation: {}, pending_prompt_type: {}, pending_close_request: {}, pending_close_wait_for_destroyed: {}, pending_close_response_mode: {}, pending_prompt_request: {}, pending_prompt_realm: {}, pending_prompt_unwrap_result: {}, pending_beforeunload_request: {}, pending_beforeunload_url: {}, pending_beforeunload_requested_url: {}, pending_beforeunload_wait: {}, pending_beforeunload_navigation: {}, pending_beforeunload_finalize_mode: {}, pending_navigation_request: {}, pending_navigation_response_mode: {}, in_flight_navigation_url: {}, in_flight_navigation_id: {}, in_flight_navigation_started_at: {}, pending_prompt_result_var: {}, context_local_storage: {}, storage_cookies_by_scope: {}, synthetic_location_href_by_context: {}, context_last_started_navigation: {}, context_requested_navigation_url: {}, context_original_opener: {}, context_realm: {}, context_sandbox_realm: {}, realm_context: {}, context_worker_realms: {}, realm_sandbox: {}, context_parent: {}, context_children: {}, context_had_synthetic_child: {}, context_client_window: {}, context_viewport_width: {}, context_viewport_height: {}, context_device_pixel_ratio: {}, input_pressed_keys: {}, input_pointer_x: {}, input_pointer_y: {}, input_pointer_buttons: {}, input_pointer_type: {}, input_pointer_target_shared: {}, input_pointer_down_x: {}, input_pointer_down_y: {}, input_pointer_down_target_shared: {}, input_pointer_dragged: {}, input_html_drag_source_shared: {}, input_html_drag_active: {}, input_html_drag_over_shared: {}, input_html_drag_accept_shared: {}, input_html_drag_transfer_id: {}, input_last_click_time: {}, input_last_click_x: {}, input_last_click_y: {}, input_last_click_target_shared: {}, input_pending_double_click: {}, input_action_clock_ms: {}, input_synthetic_events_by_context: {}, context_synthetic_scrolled: {}, speculation_prefetch_target_by_context: {}, network_state: @crater_network.BidiNetworkState::new(), socket: None, socket_epoch: 0, use_quickjs: false, dom_initialized: false, preload_scripts: [], } } ///| /// Set WebSocket for async responses and return epoch token. fn BidiProtocol::set_socket(self : BidiProtocol, socket : @core.Any) -> Int { self.socket_epoch += 1 self.socket = Some(socket) self.socket_epoch } ///| /// Clear WebSocket reference fn BidiProtocol::clear_socket(self : BidiProtocol) -> Unit { self.socket = None } ///| /// Check whether a captured WebSocket epoch is still active. fn BidiProtocol::is_current_socket_epoch( self : BidiProtocol, epoch : Int, ) -> Bool { self.socket_epoch == epoch } ///| /// Reset per-connection subscription state. fn BidiProtocol::reset_subscription_state(self : BidiProtocol) -> Unit { self.subscription_state.reset() } ///| /// Reset per-session browsing-context and realm state. fn BidiProtocol::reset_session_runtime_state(self : BidiProtocol) -> Unit { self.reset_subscription_state() for ctx_id in self.manager.list_sessions() { self.manager.close_session(ctx_id) |> ignore } self.default_context_id = None self.active_context_id = None let user_context_keys : Array[String] = [] for key, _ in self.user_contexts { user_context_keys.push(key) } for key in user_context_keys { self.user_contexts.remove(key) } self.user_contexts["default"] = true let prompt_behavior_keys : Array[String] = [] for key, _ in self.user_context_prompt_behavior { prompt_behavior_keys.push(key) } for key in prompt_behavior_keys { self.user_context_prompt_behavior.remove(key) } self.global_download_behavior = None let download_behavior_keys : Array[String] = [] for key, _ in self.user_context_download_behavior { download_behavior_keys.push(key) } for key in download_behavior_keys { self.user_context_download_behavior.remove(key) } let permission_scope_keys : Array[String] = [] for key, _ in self.permission_state_by_scope { permission_scope_keys.push(key) } for key in permission_scope_keys { self.permission_state_by_scope.remove(key) } self.emulation_state.reset() let geolocation_watch_channel_keys : Array[String] = [] for key, _ in self.geolocation_watch_channel_by_scope { geolocation_watch_channel_keys.push(key) } for key in geolocation_watch_channel_keys { self.geolocation_watch_channel_by_scope.remove(key) self.geolocation_watch_realm_by_scope.remove(key) self.geolocation_watch_notify_success_by_scope.remove(key) self.geolocation_watch_notify_error_by_scope.remove(key) } self.next_geolocation_watch_id = 1 let bluetooth_adapter_state_keys : Array[String] = [] for key, _ in self.bluetooth_adapter_state_by_context { bluetooth_adapter_state_keys.push(key) } for key in bluetooth_adapter_state_keys { self.bluetooth_adapter_state_by_context.remove(key) } let bluetooth_disabled_keys : Array[String] = [] for key, _ in self.bluetooth_simulation_disabled_by_context { bluetooth_disabled_keys.push(key) } for key in bluetooth_disabled_keys { self.bluetooth_simulation_disabled_by_context.remove(key) } let bluetooth_preconnected_keys : Array[String] = [] for key, _ in self.bluetooth_preconnected_devices_by_context { bluetooth_preconnected_keys.push(key) } for key in bluetooth_preconnected_keys { self.bluetooth_preconnected_devices_by_context.remove(key) } let bluetooth_granted_keys : Array[String] = [] for key, _ in self.bluetooth_granted_devices_by_context { bluetooth_granted_keys.push(key) } for key in bluetooth_granted_keys { self.bluetooth_granted_devices_by_context.remove(key) } let bluetooth_pending_request_keys : Array[String] = [] for key, _ in self.bluetooth_pending_prompt_request { bluetooth_pending_request_keys.push(key) } for key in bluetooth_pending_request_keys { self.bluetooth_pending_prompt_request.remove(key) } let bluetooth_pending_realm_keys : Array[String] = [] for key, _ in self.bluetooth_pending_prompt_realm { bluetooth_pending_realm_keys.push(key) } for key in bluetooth_pending_realm_keys { self.bluetooth_pending_prompt_realm.remove(key) } let bluetooth_pending_unwrap_keys : Array[String] = [] for key, _ in self.bluetooth_pending_prompt_unwrap_result { bluetooth_pending_unwrap_keys.push(key) } for key in bluetooth_pending_unwrap_keys { self.bluetooth_pending_prompt_unwrap_result.remove(key) } let bluetooth_pending_prompt_id_keys : Array[String] = [] for key, _ in self.bluetooth_pending_prompt_id { bluetooth_pending_prompt_id_keys.push(key) } for key in bluetooth_pending_prompt_id_keys { self.bluetooth_pending_prompt_id.remove(key) } let bluetooth_pending_address_keys : Array[String] = [] for key, _ in self.bluetooth_pending_prompt_device_address { bluetooth_pending_address_keys.push(key) } for key in bluetooth_pending_address_keys { self.bluetooth_pending_prompt_device_address.remove(key) } let bluetooth_pending_name_keys : Array[String] = [] for key, _ in self.bluetooth_pending_prompt_device_name { bluetooth_pending_name_keys.push(key) } for key in bluetooth_pending_name_keys { self.bluetooth_pending_prompt_device_name.remove(key) } let bluetooth_gatt_connected_keys : Array[String] = [] for key, _ in self.bluetooth_gatt_connected_by_scope { bluetooth_gatt_connected_keys.push(key) } for key in bluetooth_gatt_connected_keys { self.bluetooth_gatt_connected_by_scope.remove(key) } let bluetooth_service_scope_keys : Array[String] = [] for key, _ in self.bluetooth_services_by_scope { bluetooth_service_scope_keys.push(key) } for key in bluetooth_service_scope_keys { self.bluetooth_services_by_scope.remove(key) } let bluetooth_characteristic_scope_keys : Array[String] = [] for key, _ in self.bluetooth_characteristics_by_scope { bluetooth_characteristic_scope_keys.push(key) } for key in bluetooth_characteristic_scope_keys { self.bluetooth_characteristics_by_scope.remove(key) } let bluetooth_descriptor_scope_keys : Array[String] = [] for key, _ in self.bluetooth_descriptors_by_scope { bluetooth_descriptor_scope_keys.push(key) } for key in bluetooth_descriptor_scope_keys { self.bluetooth_descriptors_by_scope.remove(key) } let bluetooth_pending_gatt_request_keys : Array[String] = [] for key, _ in self.bluetooth_pending_gatt_request { bluetooth_pending_gatt_request_keys.push(key) } for key in bluetooth_pending_gatt_request_keys { self.bluetooth_pending_gatt_request.remove(key) } let bluetooth_pending_gatt_realm_keys : Array[String] = [] for key, _ in self.bluetooth_pending_gatt_realm { bluetooth_pending_gatt_realm_keys.push(key) } for key in bluetooth_pending_gatt_realm_keys { self.bluetooth_pending_gatt_realm.remove(key) } let bluetooth_pending_gatt_unwrap_keys : Array[String] = [] for key, _ in self.bluetooth_pending_gatt_unwrap_result { bluetooth_pending_gatt_unwrap_keys.push(key) } for key in bluetooth_pending_gatt_unwrap_keys { self.bluetooth_pending_gatt_unwrap_result.remove(key) } let bluetooth_pending_gatt_address_keys : Array[String] = [] for key, _ in self.bluetooth_pending_gatt_address { bluetooth_pending_gatt_address_keys.push(key) } for key in bluetooth_pending_gatt_address_keys { self.bluetooth_pending_gatt_address.remove(key) } let bluetooth_pending_gatt_kind_keys : Array[String] = [] for key, _ in self.bluetooth_pending_gatt_response_kind { bluetooth_pending_gatt_kind_keys.push(key) } for key in bluetooth_pending_gatt_kind_keys { self.bluetooth_pending_gatt_response_kind.remove(key) } let bluetooth_pending_characteristic_request_keys : Array[String] = [] for key, _ in self.bluetooth_pending_characteristic_request { bluetooth_pending_characteristic_request_keys.push(key) } for key in bluetooth_pending_characteristic_request_keys { self.bluetooth_pending_characteristic_request.remove(key) } let bluetooth_pending_characteristic_realm_keys : Array[String] = [] for key, _ in self.bluetooth_pending_characteristic_realm { bluetooth_pending_characteristic_realm_keys.push(key) } for key in bluetooth_pending_characteristic_realm_keys { self.bluetooth_pending_characteristic_realm.remove(key) } let bluetooth_pending_characteristic_unwrap_keys : Array[String] = [] for key, _ in self.bluetooth_pending_characteristic_unwrap_result { bluetooth_pending_characteristic_unwrap_keys.push(key) } for key in bluetooth_pending_characteristic_unwrap_keys { self.bluetooth_pending_characteristic_unwrap_result.remove(key) } let bluetooth_pending_characteristic_address_keys : Array[String] = [] for key, _ in self.bluetooth_pending_characteristic_address { bluetooth_pending_characteristic_address_keys.push(key) } for key in bluetooth_pending_characteristic_address_keys { self.bluetooth_pending_characteristic_address.remove(key) } let bluetooth_pending_characteristic_service_keys : Array[String] = [] for key, _ in self.bluetooth_pending_characteristic_service_uuid { bluetooth_pending_characteristic_service_keys.push(key) } for key in bluetooth_pending_characteristic_service_keys { self.bluetooth_pending_characteristic_service_uuid.remove(key) } let bluetooth_pending_characteristic_uuid_keys : Array[String] = [] for key, _ in self.bluetooth_pending_characteristic_uuid { bluetooth_pending_characteristic_uuid_keys.push(key) } for key in bluetooth_pending_characteristic_uuid_keys { self.bluetooth_pending_characteristic_uuid.remove(key) } let bluetooth_pending_characteristic_type_keys : Array[String] = [] for key, _ in self.bluetooth_pending_characteristic_type { bluetooth_pending_characteristic_type_keys.push(key) } for key in bluetooth_pending_characteristic_type_keys { self.bluetooth_pending_characteristic_type.remove(key) } let bluetooth_pending_descriptor_request_keys : Array[String] = [] for key, _ in self.bluetooth_pending_descriptor_request { bluetooth_pending_descriptor_request_keys.push(key) } for key in bluetooth_pending_descriptor_request_keys { self.bluetooth_pending_descriptor_request.remove(key) } let bluetooth_pending_descriptor_realm_keys : Array[String] = [] for key, _ in self.bluetooth_pending_descriptor_realm { bluetooth_pending_descriptor_realm_keys.push(key) } for key in bluetooth_pending_descriptor_realm_keys { self.bluetooth_pending_descriptor_realm.remove(key) } let bluetooth_pending_descriptor_unwrap_keys : Array[String] = [] for key, _ in self.bluetooth_pending_descriptor_unwrap_result { bluetooth_pending_descriptor_unwrap_keys.push(key) } for key in bluetooth_pending_descriptor_unwrap_keys { self.bluetooth_pending_descriptor_unwrap_result.remove(key) } let bluetooth_pending_descriptor_address_keys : Array[String] = [] for key, _ in self.bluetooth_pending_descriptor_address { bluetooth_pending_descriptor_address_keys.push(key) } for key in bluetooth_pending_descriptor_address_keys { self.bluetooth_pending_descriptor_address.remove(key) } let bluetooth_pending_descriptor_service_keys : Array[String] = [] for key, _ in self.bluetooth_pending_descriptor_service_uuid { bluetooth_pending_descriptor_service_keys.push(key) } for key in bluetooth_pending_descriptor_service_keys { self.bluetooth_pending_descriptor_service_uuid.remove(key) } let bluetooth_pending_descriptor_characteristic_keys : Array[String] = [] for key, _ in self.bluetooth_pending_descriptor_characteristic_uuid { bluetooth_pending_descriptor_characteristic_keys.push(key) } for key in bluetooth_pending_descriptor_characteristic_keys { self.bluetooth_pending_descriptor_characteristic_uuid.remove(key) } let bluetooth_pending_descriptor_uuid_keys : Array[String] = [] for key, _ in self.bluetooth_pending_descriptor_uuid { bluetooth_pending_descriptor_uuid_keys.push(key) } for key in bluetooth_pending_descriptor_uuid_keys { self.bluetooth_pending_descriptor_uuid.remove(key) } let bluetooth_pending_descriptor_type_keys : Array[String] = [] for key, _ in self.bluetooth_pending_descriptor_type { bluetooth_pending_descriptor_type_keys.push(key) } for key in bluetooth_pending_descriptor_type_keys { self.bluetooth_pending_descriptor_type.remove(key) } self.next_bluetooth_prompt_id = 1 let user_context_viewport_width_keys : Array[String] = [] for key, _ in self.user_context_viewport_width { user_context_viewport_width_keys.push(key) } for key in user_context_viewport_width_keys { self.user_context_viewport_width.remove(key) } let user_context_viewport_height_keys : Array[String] = [] for key, _ in self.user_context_viewport_height { user_context_viewport_height_keys.push(key) } for key in user_context_viewport_height_keys { self.user_context_viewport_height.remove(key) } let user_context_device_pixel_ratio_keys : Array[String] = [] for key, _ in self.user_context_device_pixel_ratio { user_context_device_pixel_ratio_keys.push(key) } for key in user_context_device_pixel_ratio_keys { self.user_context_device_pixel_ratio.remove(key) } let context_user_keys : Array[String] = [] for key, _ in self.context_user_context { context_user_keys.push(key) } for key in context_user_keys { self.context_user_context.remove(key) } let context_beforeunload_keys : Array[String] = [] for key, _ in self.context_has_beforeunload { context_beforeunload_keys.push(key) } for key in context_beforeunload_keys { self.context_has_beforeunload.remove(key) } let context_policy_keys : Array[String] = [] for key, _ in self.context_blocks_cross_origin_iframe_navigation { context_policy_keys.push(key) } for key in context_policy_keys { self.context_blocks_cross_origin_iframe_navigation.remove(key) } let pending_prompt_keys : Array[String] = [] for key, _ in self.pending_prompt_type { pending_prompt_keys.push(key) } for key in pending_prompt_keys { self.pending_prompt_type.remove(key) self.pending_close_request.remove(key) self.pending_prompt_request.remove(key) self.pending_prompt_realm.remove(key) self.pending_beforeunload_request.remove(key) self.pending_beforeunload_url.remove(key) self.pending_beforeunload_requested_url.remove(key) self.pending_beforeunload_wait.remove(key) self.pending_beforeunload_navigation.remove(key) self.pending_prompt_result_var.remove(key) } let in_flight_navigation_keys : Array[String] = [] for key, _ in self.in_flight_navigation_id { in_flight_navigation_keys.push(key) } for key in in_flight_navigation_keys { self.clear_pending_navigation_state(key) } let context_local_storage_keys : Array[String] = [] for key, _ in self.context_local_storage { context_local_storage_keys.push(key) } for key in context_local_storage_keys { self.context_local_storage.remove(key) } let storage_cookie_scope_keys : Array[String] = [] for key, _ in self.storage_cookies_by_scope { storage_cookie_scope_keys.push(key) } for key in storage_cookie_scope_keys { self.storage_cookies_by_scope.remove(key) } self.network_state.next_network_request_id = 1 let synthetic_location_keys : Array[String] = [] for key, _ in self.synthetic_location_href_by_context { synthetic_location_keys.push(key) } for key in synthetic_location_keys { self.synthetic_location_href_by_context.remove(key) } let last_navigation_keys : Array[String] = [] for key, _ in self.context_last_started_navigation { last_navigation_keys.push(key) } for key in last_navigation_keys { self.context_last_started_navigation.remove(key) } let requested_navigation_keys : Array[String] = [] for key, _ in self.context_requested_navigation_url { requested_navigation_keys.push(key) } for key in requested_navigation_keys { self.context_requested_navigation_url.remove(key) } let context_original_opener_keys : Array[String] = [] for key, _ in self.context_original_opener { context_original_opener_keys.push(key) } for key in context_original_opener_keys { self.context_original_opener.remove(key) } let context_realm_keys : Array[String] = [] for key, _ in self.context_realm { context_realm_keys.push(key) } for key in context_realm_keys { self.context_realm.remove(key) } let context_sandbox_realm_keys : Array[String] = [] for key, _ in self.context_sandbox_realm { context_sandbox_realm_keys.push(key) } for key in context_sandbox_realm_keys { self.context_sandbox_realm.remove(key) } let realm_context_keys : Array[String] = [] for key, _ in self.realm_context { realm_context_keys.push(key) } for key in realm_context_keys { self.realm_context.remove(key) } let context_worker_realm_keys : Array[String] = [] for key, _ in self.context_worker_realms { context_worker_realm_keys.push(key) } for key in context_worker_realm_keys { self.context_worker_realms.remove(key) } let realm_sandbox_keys : Array[String] = [] for key, _ in self.realm_sandbox { realm_sandbox_keys.push(key) } for key in realm_sandbox_keys { self.realm_sandbox.remove(key) } let context_parent_keys : Array[String] = [] for key, _ in self.context_parent { context_parent_keys.push(key) } for key in context_parent_keys { self.context_parent.remove(key) } let context_children_keys : Array[String] = [] for key, _ in self.context_children { context_children_keys.push(key) } for key in context_children_keys { self.context_children.remove(key) } let synthetic_child_context_keys : Array[String] = [] for key, _ in self.context_had_synthetic_child { synthetic_child_context_keys.push(key) } for key in synthetic_child_context_keys { self.context_had_synthetic_child.remove(key) } let context_client_window_keys : Array[String] = [] for key, _ in self.context_client_window { context_client_window_keys.push(key) } for key in context_client_window_keys { self.context_client_window.remove(key) } let network_state_channel_keys : Array[String] = [] for key, _ in self.network_state.network_state_channel_by_context { network_state_channel_keys.push(key) } for key in network_state_channel_keys { self.network_state.network_state_channel_by_context.remove(key) } let context_viewport_width_keys : Array[String] = [] for key, _ in self.context_viewport_width { context_viewport_width_keys.push(key) } for key in context_viewport_width_keys { self.context_viewport_width.remove(key) } let context_viewport_height_keys : Array[String] = [] for key, _ in self.context_viewport_height { context_viewport_height_keys.push(key) } for key in context_viewport_height_keys { self.context_viewport_height.remove(key) } let context_device_pixel_ratio_keys : Array[String] = [] for key, _ in self.context_device_pixel_ratio { context_device_pixel_ratio_keys.push(key) } for key in context_device_pixel_ratio_keys { self.context_device_pixel_ratio.remove(key) } let input_pressed_keys : Array[String] = [] for key, _ in self.input_pressed_keys { input_pressed_keys.push(key) } for key in input_pressed_keys { self.input_pressed_keys.remove(key) } let input_pointer_x_keys : Array[String] = [] for key, _ in self.input_pointer_x { input_pointer_x_keys.push(key) } for key in input_pointer_x_keys { self.input_pointer_x.remove(key) } let input_pointer_y_keys : Array[String] = [] for key, _ in self.input_pointer_y { input_pointer_y_keys.push(key) } for key in input_pointer_y_keys { self.input_pointer_y.remove(key) } let input_pointer_button_keys : Array[String] = [] for key, _ in self.input_pointer_buttons { input_pointer_button_keys.push(key) } for key in input_pointer_button_keys { self.input_pointer_buttons.remove(key) } let input_pointer_type_keys : Array[String] = [] for key, _ in self.input_pointer_type { input_pointer_type_keys.push(key) } for key in input_pointer_type_keys { self.input_pointer_type.remove(key) } let input_pointer_target_keys : Array[String] = [] for key, _ in self.input_pointer_target_shared { input_pointer_target_keys.push(key) } for key in input_pointer_target_keys { self.input_pointer_target_shared.remove(key) } let input_pointer_down_x_keys : Array[String] = [] for key, _ in self.input_pointer_down_x { input_pointer_down_x_keys.push(key) } for key in input_pointer_down_x_keys { self.input_pointer_down_x.remove(key) } let input_pointer_down_y_keys : Array[String] = [] for key, _ in self.input_pointer_down_y { input_pointer_down_y_keys.push(key) } for key in input_pointer_down_y_keys { self.input_pointer_down_y.remove(key) } let input_pointer_down_target_keys : Array[String] = [] for key, _ in self.input_pointer_down_target_shared { input_pointer_down_target_keys.push(key) } for key in input_pointer_down_target_keys { self.input_pointer_down_target_shared.remove(key) } let input_pointer_dragged_keys : Array[String] = [] for key, _ in self.input_pointer_dragged { input_pointer_dragged_keys.push(key) } for key in input_pointer_dragged_keys { self.input_pointer_dragged.remove(key) } let input_html_drag_source_keys : Array[String] = [] for key, _ in self.input_html_drag_source_shared { input_html_drag_source_keys.push(key) } for key in input_html_drag_source_keys { self.input_html_drag_source_shared.remove(key) } let input_html_drag_active_keys : Array[String] = [] for key, _ in self.input_html_drag_active { input_html_drag_active_keys.push(key) } for key in input_html_drag_active_keys { self.input_html_drag_active.remove(key) } let input_html_drag_over_keys : Array[String] = [] for key, _ in self.input_html_drag_over_shared { input_html_drag_over_keys.push(key) } for key in input_html_drag_over_keys { self.input_html_drag_over_shared.remove(key) } let input_html_drag_accept_keys : Array[String] = [] for key, _ in self.input_html_drag_accept_shared { input_html_drag_accept_keys.push(key) } for key in input_html_drag_accept_keys { self.input_html_drag_accept_shared.remove(key) } let input_html_drag_transfer_keys : Array[String] = [] for key, _ in self.input_html_drag_transfer_id { input_html_drag_transfer_keys.push(key) } for key in input_html_drag_transfer_keys { let transfer_id = self.input_html_drag_transfer_id.get(key).unwrap_or("") if transfer_id != "" { input_clear_drag_transfer(transfer_id) } self.input_html_drag_transfer_id.remove(key) } let input_last_click_time_keys : Array[String] = [] for key, _ in self.input_last_click_time { input_last_click_time_keys.push(key) } for key in input_last_click_time_keys { self.input_last_click_time.remove(key) } let input_last_click_x_keys : Array[String] = [] for key, _ in self.input_last_click_x { input_last_click_x_keys.push(key) } for key in input_last_click_x_keys { self.input_last_click_x.remove(key) } let input_last_click_y_keys : Array[String] = [] for key, _ in self.input_last_click_y { input_last_click_y_keys.push(key) } for key in input_last_click_y_keys { self.input_last_click_y.remove(key) } let input_last_click_target_keys : Array[String] = [] for key, _ in self.input_last_click_target_shared { input_last_click_target_keys.push(key) } for key in input_last_click_target_keys { self.input_last_click_target_shared.remove(key) } let input_pending_double_click_keys : Array[String] = [] for key, _ in self.input_pending_double_click { input_pending_double_click_keys.push(key) } for key in input_pending_double_click_keys { self.input_pending_double_click.remove(key) } let input_action_clock_keys : Array[String] = [] for key, _ in self.input_action_clock_ms { input_action_clock_keys.push(key) } for key in input_action_clock_keys { self.input_action_clock_ms.remove(key) } let input_synthetic_event_keys : Array[String] = [] for key, _ in self.input_synthetic_events_by_context { input_synthetic_event_keys.push(key) } for key in input_synthetic_event_keys { self.input_synthetic_events_by_context.remove(key) } let speculation_prefetch_target_keys : Array[String] = [] for key, _ in self.speculation_prefetch_target_by_context { speculation_prefetch_target_keys.push(key) } for key in speculation_prefetch_target_keys { self.speculation_prefetch_target_by_context.remove(key) } let intercept_keys : Array[String] = [] for key, _ in self.network_state.network_intercepts { intercept_keys.push(key) } for key in intercept_keys { self.network_state.network_intercepts.remove(key) } self.network_state.next_network_intercept_id = 1 self.clear_all_network_state() let collector_keys : Array[String] = [] for key, _ in self.network_state.network_data_collectors { collector_keys.push(key) } for key in collector_keys { self.network_state.network_data_collectors.remove(key) } let collected_request_keys : Array[String] = [] for key, _ in self.network_state.network_collected_data { collected_request_keys.push(key) } for key in collected_request_keys { self.network_state.network_collected_data.remove(key) } self.network_state.next_network_data_collector_id = 1 self.next_preload_script_id = 1 let installed_web_extension_keys : Array[String] = [] for key, _ in self.installed_web_extensions { installed_web_extension_keys.push(key) } for key in installed_web_extension_keys { self.installed_web_extensions.remove(key) } self.next_web_extension_id = 1 self.preload_scripts = [] } ///| fn BidiProtocol::handle_network_prepare_context_for_test( 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 ctx_id = match map.get("context") { Some(String(ctx_id)) => if ctx_id == "" || self.manager.get_session(ctx_id) is None { self.send_error( request_id, "no such frame", "Unknown context: " + ctx_id, ) return } else { ctx_id } Some(Null) | None => self.ensure_default_context() _ => { self.send_error( request_id, "invalid argument", "context must be a string", ) return } } let target_url = match map.get("url") { Some(String(url)) => url _ => { self.send_error(request_id, "invalid argument", "url must be a string") return } } let resolved_url = self.resolve_effective_navigation_target_url( ctx_id, target_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", ) match self.manager.get_session(ctx_id) { Some(session) => { let previous_url = session.get_url() match session.navigate_to(resolved_url) { Ok(_) => self.complete_navigate_with_state( request_id, ctx_id, target_url, resolved_url, previous_url, "complete", None, ) Err(_) => self.send_success( request_id, Some( make_object({ "context": Json::string(ctx_id), "url": Json::string(self.current_context_url(ctx_id)), "navigated": Json::boolean(false), }), ), ) } } None => { self.send_error(request_id, "no such frame", "Unknown context: " + ctx_id) return } } } ///| /// Left-pad a string with '0' up to min_length. fn left_pad_with_zero(value : String, min_length : Int) -> String { let mut result = value while result.length() < min_length { result = "0" + result } result } ///| /// Remove one occurrence for each event in `events_to_remove`. fn array_remove_events_once( arr : Array[String], events_to_remove : Array[String], ) -> Array[String] { let mut result = arr for evt in events_to_remove { result = array_remove_first(result, evt) } result } ///| /// Remove the first matching item from array. fn array_remove_first(arr : Array[String], target : String) -> Array[String] { let result : Array[String] = [] let mut removed = false for item in arr { if !removed && item == target { removed = true continue } result.push(item) } result } ///| /// Check if array contains a string fn array_contains(arr : Array[String], item : String) -> Bool { for s in arr { if s == item { return true } } false } ///| /// Filter array to exclude items in exclude list fn array_filter_not( arr : Array[String], exclude : Array[String], ) -> Array[String] { let result : Array[String] = [] for s in arr { if !array_contains(exclude, s) { result.push(s) } } result } ///| /// Filter array to keep events outside the given module fn array_filter_by_module( arr : Array[String], module_key : String, ) -> Array[String] { let result : Array[String] = [] for s in arr { if get_subscription_key(s) != module_key { result.push(s) } } result } ///| /// Handle top-level `window.open(...)` evaluate calls and keep browsing context /// state synchronized with BiDi events. fn BidiProtocol::try_handle_window_open( self : BidiProtocol, request_id : Int, opener_ctx_id : String, opener_realm_id : String, expression : String, unwrap_result : Bool, ) -> Bool { let trimmed = expression.trim().to_owned() let open_marker_idx = match find_substring(trimmed, "window.open(", 0) { Some(idx) => idx None => return false } let open_call_source = trimmed.unsafe_substring( start=open_marker_idx, end=trimmed.length(), ) let opened_url = extract_quoted_after_marker(open_call_source, "window.open(").unwrap_or( "about:blank", ) let resolved_opened_url = if opened_url == "" { "about:blank" } else { opened_url } let skip_navigation_started = resolved_opened_url == "about:blank" || resolved_opened_url.has_prefix("about:blank?") let user_ctx = self.context_user_context .get(opener_ctx_id) .unwrap_or("default") let new_ctx_id = self.create_context(user_ctx, "tab") self.context_original_opener[new_ctx_id] = opener_ctx_id // WPT expects contextCreated.url to stay about:blank for window.open flows. self.emit_context_created( new_ctx_id, "tab", "about:blank", Some(opener_ctx_id), None, ) match self.manager.get_session(new_ctx_id) { Some(session) => { let _ = session.navigate_to(resolved_opened_url) } None => () } self.load_data_url_content(new_ctx_id, resolved_opened_url) self.drop_child_contexts(new_ctx_id, false) self.refresh_child_contexts_from_iframes(new_ctx_id) self.clear_context_sandbox_realms(new_ctx_id) let opened_realm_id = self.assign_new_realm(new_ctx_id) self.emit_default_realm(new_ctx_id) self.emit_worker_realms_from_scripts(new_ctx_id, opened_realm_id) self.apply_preload_scripts_for_context(new_ctx_id) self.execute_inline_scripts_for_context(new_ctx_id) let mirror_opened_foo = trimmed.contains("window.baz") && trimmed.contains("window.open(") && trimmed.contains(".foo") if mirror_opened_foo { set_runtime_context(new_ctx_id) self.apply_effective_viewport_to_runtime_context(new_ctx_id, new_ctx_id) let opened_eval_json = evaluate_js_with_console( "window.foo", false, false, false, "{}", ) let opened_eval = @json.parse(opened_eval_json) catch { _ => make_object({ "result": make_object({ "type": Json::string("undefined") }), }) } let opened_value = get_field(opened_eval, "result").unwrap_or( make_object({ "type": Json::string("undefined") }), ) set_runtime_context(opener_ctx_id) self.apply_effective_viewport_to_runtime_context( opener_ctx_id, opener_ctx_id, ) evaluate_js("window.baz = " + bidi_value_to_js_literal(opened_value)) |> ignore } if !skip_navigation_started { let nav_id = self.next_navigation_id.to_string() self.next_navigation_id += 1 self.emit_navigation_events(new_ctx_id, resolved_opened_url, nav_id) } let returns_null = trimmed.contains("noopener") || trimmed.contains("noreferrer") let remote_value = if returns_null { make_object({ "type": Json::string("null") }) } else { make_object({ "type": Json::string("window"), "value": make_object({ "context": Json::string(new_ctx_id) }), }) } self.send_script_remote_value_response( request_id, opener_realm_id, remote_value, unwrap_result, ) true } ///| /// Handle synthetic iframe creation expressions used by preload execution-order tests. fn BidiProtocol::try_handle_synthetic_iframe_creation( self : BidiProtocol, request_id : Int, parent_ctx_id : String, realm_id : String, expression : String, ) -> Bool { let trimmed = expression.trim().to_owned() // Detection: any `createElement('iframe')` (single or double quotes, // optional internal whitespace) followed somewhere by ANY appendChild // call. The previous matcher required the literal `appendChild(iframe)` // — that missed every real-app pattern that names the element variable // anything other than `iframe`, uses a non-document.body parent, or // appends via insertBefore / replaceChild / prepend. Loosening to // "anything that ends in some kind of append" catches the common cases // without false-positive risk (the createElement gate is the strong // condition). let creates_iframe_element = trimmed.contains("createElement('iframe')") || trimmed.contains("createElement(\"iframe\")") let has_dom_insertion = trimmed.contains("appendChild(") || trimmed.contains("insertBefore(") || trimmed.contains("replaceChild(") || trimmed.contains("prepend(") || trimmed.contains("append(") || trimmed.contains("replaceWith(") || trimmed.contains("before(") || trimmed.contains("after(") if !creates_iframe_element || !has_dom_insertion { return false } // src extraction: try the canonical `iframe.src=...` form first // (kept for back-compat with the WPT shape), then progressively wider // variants — `.src = ...` with surrounding whitespace, `.srcdoc`, // `setAttribute('src', ...)`. Each variant returns Some when matched; // first match wins. Fall back to about:blank. let raw_src = match extract_quoted_after_marker(trimmed, "iframe.src=") { Some(v) if v != "" => v _ => match extract_quoted_after_marker(trimmed, ".src = ") { Some(v) if v != "" => v _ => match extract_quoted_after_marker(trimmed, ".src=") { Some(v) if v != "" => v _ => match extract_quoted_after_marker(trimmed, ".src =") { Some(v) if v != "" => v _ => match extract_second_quoted_after_marker( trimmed, "setAttribute('src'", ) { Some(v) if v != "" => v _ => match extract_second_quoted_after_marker( trimmed, "setAttribute(\"src\"", ) { Some(v) if v != "" => v _ => // srcdoc path — inline HTML, no real URL. Per // spec, srcdoc renders as `about:srcdoc`. if trimmed.contains(".srcdoc") { "about:srcdoc" } else { "about:blank" } } } } } } } let src = if raw_src == "" { "about:blank" } else { raw_src } let user_ctx = self.context_user_context .get(parent_ctx_id) .unwrap_or("default") let child_ctx_id = self.create_child_context(parent_ctx_id, user_ctx) self.emit_context_created( child_ctx_id, "tab", "about:blank", None, Some(parent_ctx_id), ) let child_nav_id = self.next_navigation_id.to_string() self.next_navigation_id += 1 if self.is_iframe_navigation_blocked(child_ctx_id, src) { self.emit_default_realm(child_ctx_id) self.emit_navigation_started_event(child_ctx_id, src, child_nav_id) self.emit_navigation_failed_event(child_ctx_id, src, child_nav_id) self.send_script_undefined_result(request_id, realm_id) return true } match self.manager.get_session(child_ctx_id) { Some(child_session) => { let _ = child_session.navigate_to(src) } None => () } self.load_data_url_content(child_ctx_id, src) self.clear_context_sandbox_realms(child_ctx_id) let window_realm_id = self.assign_new_realm(child_ctx_id) self.emit_default_realm(child_ctx_id) self.emit_worker_realms_from_scripts(child_ctx_id, window_realm_id) self.apply_preload_scripts_for_context(child_ctx_id) self.execute_inline_scripts_for_context(child_ctx_id) self.emit_navigation_events(child_ctx_id, src, child_nav_id) if src != "about:blank" { self.refresh_child_contexts_from_iframes_with_depth(child_ctx_id, 1) } // Cross-frame mirror: copy `.contentWindow.` from the // child realm into a parent-realm LHS. The pattern parser handles // ANY identifier name on either side, not just the hardcoded // `window.baz = iframe.contentWindow.foo` WPT shape (#182 slice 5). match extract_iframe_content_window_mirror(trimmed) { Some((lhs, prop)) => { set_runtime_context(child_ctx_id) self.apply_effective_viewport_to_runtime_context( child_ctx_id, child_ctx_id, ) let opened_eval_json = evaluate_js_with_console( "window." + prop, false, false, false, "{}", ) let opened_eval = @json.parse(opened_eval_json) catch { _ => make_object({ "result": make_object({ "type": Json::string("undefined") }), }) } let opened_value = get_field(opened_eval, "result").unwrap_or( make_object({ "type": Json::string("undefined") }), ) set_runtime_context(parent_ctx_id) self.apply_effective_viewport_to_runtime_context( parent_ctx_id, parent_ctx_id, ) evaluate_js(lhs + " = " + bidi_value_to_js_literal(opened_value)) |> ignore } None => () } self.send_script_undefined_result(request_id, realm_id) true }