-module(chrobot@chrome). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([listen_once/3, add_listener/2, remove_listener/2, set_log_level/2, send/3, get_default_chrome_args/0, is_local_chrome_path/2, get_local_chrome_path_at/1, get_local_chrome_path/0, process_port_message/2, get_system_chrome_path/0, call/5, launch_with_config/1, quit/1, get_version/1, resolve_env_cofig/0, launch/0, launch_window/0, launch_with_env/0]). -export_type([log_level/0, browser_config/0, browser_instance/0, browser_version/0, launch_error/0, request_error/0, browser_state/0, message/0, pending_request/0, browser_response/0, raw_browser_error/0]). -type log_level() :: log_level_silent | log_level_warnings | log_level_info | log_level_debug. -type browser_config() :: {browser_config, binary(), list(binary()), integer(), log_level()}. -type browser_instance() :: {browser_instance, gleam@otp@port:port_()}. -type browser_version() :: {browser_version, binary(), binary(), binary(), binary(), binary()}. -type launch_error() :: unknow_operating_system | could_not_find_executable | failed_to_start | unresponsive_after_start | {protocol_version_mismatch, binary(), binary()}. -type request_error() :: port_error | chrome_agent_timeout | chrome_agent_down | protocol_error | {browser_error, integer(), binary(), binary()} | not_found_error | {runtime_exception, binary(), integer(), integer()}. -type browser_state() :: {browser_state, browser_instance(), integer(), list(pending_request()), list({binary(), gleam@erlang@process:subject(gleam@dynamic:dynamic_())}), gleam@string_builder:string_builder(), gleam@option:option(gleam@erlang@process:subject(nil)), log_level()}. -type message() :: {shutdown, gleam@erlang@process:subject(nil)} | kill | {call, gleam@erlang@process:subject({ok, gleam@dynamic:dynamic_()} | {error, request_error()}), binary(), gleam@option:option(gleam@json:json()), gleam@option:option(binary())} | {send, binary(), gleam@option:option(gleam@json:json())} | {add_listener, gleam@erlang@process:subject(gleam@dynamic:dynamic_()), binary()} | {remove_listener, gleam@erlang@process:subject(gleam@dynamic:dynamic_())} | {unexpected_port_message, gleam@dynamic:dynamic_()} | {port_response, binary()} | {set_log_level, log_level()} | {port_exit, integer()}. -type pending_request() :: {pending_request, integer(), gleam@erlang@process:subject({ok, gleam@dynamic:dynamic_()} | {error, request_error()})}. -type browser_response() :: {browser_response, gleam@option:option(integer()), gleam@option:option(gleam@dynamic:dynamic_()), gleam@option:option(binary()), gleam@option:option(gleam@dynamic:dynamic_()), gleam@option:option(raw_browser_error())}. -type raw_browser_error() :: {raw_browser_error, gleam@option:option(integer()), gleam@option:option(binary()), gleam@option:option(binary())}. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 288). -spec listen_once(gleam@erlang@process:subject(message()), binary(), integer()) -> {ok, gleam@dynamic:dynamic_()} | {error, request_error()}. listen_once(Browser, Method, Time_out) -> Event_subject = gleam@erlang@process:new_subject(), Call_response = chrobot@internal@utils:try_call_with_subject( Browser, fun(_capture) -> {add_listener, _capture, Method} end, Event_subject, Time_out ), gleam@erlang@process:send(Browser, {remove_listener, Event_subject}), case Call_response of {ok, Res} -> {ok, Res}; {error, call_timeout} -> {error, chrome_agent_timeout}; {error, {callee_down, _}} -> {error, chrome_agent_down} end. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 311). -spec add_listener(gleam@erlang@process:subject(message()), binary()) -> gleam@erlang@process:subject(gleam@dynamic:dynamic_()). add_listener(Browser, Method) -> Event_subject = gleam@erlang@process:new_subject(), gleam@erlang@process:send(Browser, {add_listener, Event_subject, Method}), Event_subject. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 319). -spec remove_listener( gleam@erlang@process:subject(message()), gleam@erlang@process:subject(gleam@dynamic:dynamic_()) ) -> nil. remove_listener(Browser, Listener) -> gleam@erlang@process:send(Browser, {remove_listener, Listener}). -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 324). -spec set_log_level(gleam@erlang@process:subject(message()), log_level()) -> nil. set_log_level(Browser, Level) -> gleam@erlang@process:send(Browser, {set_log_level, Level}). -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 333). -spec send( gleam@erlang@process:subject(message()), binary(), gleam@option:option(gleam@json:json()) ) -> nil. send(Browser, Method, Params) -> gleam@erlang@process:send(Browser, {send, Method, Params}). -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 370). -spec get_default_chrome_args() -> list(binary()). get_default_chrome_args() -> [<<"--headless"/utf8>>, <<"--disable-accelerated-2d-canvas"/utf8>>, <<"--disable-gpu"/utf8>>, <<"--allow-pre-commit-input"/utf8>>, <<"--disable-background-networking"/utf8>>, <<"--disable-background-timer-throttling"/utf8>>, <<"--disable-backgrounding-occluded-windows"/utf8>>, <<"--disable-breakpad"/utf8>>, <<"--disable-client-side-phishing-detection"/utf8>>, <<"--disable-component-extensions-with-background-pages"/utf8>>, <<"--disable-component-update"/utf8>>, <<"--disable-default-apps"/utf8>>, <<"--disable-extensions"/utf8>>, <<"--disable-features=Translate,BackForwardCache,AcceptCHFrame,MediaRouter,OptimizationHints"/utf8>>, <<"--disable-hang-monitor"/utf8>>, <<"--disable-ipc-flooding-protection"/utf8>>, <<"--disable-popup-blocking"/utf8>>, <<"--disable-prompt-on-repost"/utf8>>, <<"--disable-renderer-backgrounding"/utf8>>, <<"--disable-sync"/utf8>>, <<"--enable-automation"/utf8>>, <<"--enable-features=NetworkServiceInProcess2"/utf8>>, <<"--export-tagged-pdf"/utf8>>, <<"--force-color-profile=srgb"/utf8>>, <<"--hide-scrollbars"/utf8>>, <<"--metrics-recording-only"/utf8>>, <<"--no-default-browser-check"/utf8>>, <<"--no-first-run"/utf8>>, <<"--no-service-autorun"/utf8>>, <<"--password-store=basic"/utf8>>, <<"--use-mock-keychain"/utf8>>]. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 394). -spec is_local_chrome_path(binary(), gleam@erlang@os:os_family()) -> boolean(). is_local_chrome_path(Relative_path, Os_family) -> case {Os_family, filepath:split(Relative_path)} of {darwin, [<<"chrome"/utf8>>, <<"mac"/utf8, _/binary>>, <<"chrome-"/utf8, _/binary>>, <<"Google Chrom"/utf8, _/binary>>, <<"Contents"/utf8>>, <<"MacOS"/utf8>>, <<"Google Chrom"/utf8, _/binary>>]} -> true; {linux, [<<"chrome"/utf8>>, <<"linux"/utf8, _/binary>>, <<"chrome-"/utf8, _/binary>>, <<"chrome"/utf8>>]} -> true; {windows_nt, [<<"chrome"/utf8>>, <<"win"/utf8, _/binary>>, <<"chrome-"/utf8, _/binary>>, <<"chrome.exe"/utf8>>]} -> true; {_, _} -> false end. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 431). -spec get_local_chrome_path_at(binary()) -> {ok, binary()} | {error, launch_error()}. get_local_chrome_path_at(Base_dir) -> case simplifile_erl:is_directory(Base_dir) of {ok, true} -> Files_res = gleam@result:replace_error( simplifile:get_files(Base_dir), could_not_find_executable ), gleam@result:'try'( Files_res, fun(Files) -> _pipe = gleam@list:find( Files, fun(File) -> is_local_chrome_path( File, gleam_erlang_ffi:os_family() ) end ), gleam@result:replace_error(_pipe, could_not_find_executable) end ); _ -> {error, could_not_find_executable} end. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 426). -spec get_local_chrome_path() -> {ok, binary()} | {error, launch_error()}. get_local_chrome_path() -> get_local_chrome_path_at(<<"chrome"/utf8>>). -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 523). -spec map_non_data_port_msg(gleam@dynamic:dynamic_()) -> message(). map_non_data_port_msg(Msg) -> Decoded_msg = (gleam@dynamic:tuple2( fun gleam_erlang_ffi:atom_from_dynamic/1, fun gleam@dynamic:int/1 ))(Msg), case Decoded_msg of {ok, {Atom_exit_status, Exit_status}} -> case Atom_exit_status =:= erlang:binary_to_atom( <<"exit_status"/utf8>> ) of true -> {port_exit, Exit_status}; false -> {unexpected_port_message, Msg} end; {error, _} -> {unexpected_port_message, Msg} end. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 506). -spec map_port_message(gleam@dynamic:dynamic_()) -> message(). map_port_message(Message) -> case (gleam@dynamic:element(1, fun gleam@dynamic:string/1))(Message) of {ok, Data} -> {port_response, Data}; {error, _} -> map_non_data_port_msg(Message) end. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 564). -spec process_port_message_parts(list(binary()), list(binary())) -> {list(binary()), gleam@string_builder:string_builder()}. process_port_message_parts(Parts, Collector) -> case Parts of [<<""/utf8>>] -> {lists:reverse(Collector), gleam@string_builder:new()}; [Overflow] -> {lists:reverse(Collector), begin _pipe = gleam@string_builder:new(), gleam@string_builder:append(_pipe, Overflow) end}; [Cur | Rest] -> process_port_message_parts(Rest, [Cur | Collector]); [] -> {lists:reverse(Collector), gleam@string_builder:new()} end. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 539). -spec process_port_message(binary(), gleam@string_builder:string_builder()) -> {list(binary()), gleam@string_builder:string_builder()}. process_port_message(Input, Buffer) -> case gleam@string:split(Input, <<"\x{0000}"/utf8>>) of [Unterminated_msg] -> {[], gleam@string_builder:append(Buffer, Unterminated_msg)}; [Single_msg, <<""/utf8>>] -> {[begin _pipe = gleam@string_builder:append(Buffer, Single_msg), gleam@string_builder:to_string(_pipe) end], gleam@string_builder:new()}; [First | Rest] -> Complete_parts = [begin _pipe@1 = gleam@string_builder:append(Buffer, First), gleam@string_builder:to_string(_pipe@1) end | Rest], process_port_message_parts(Complete_parts, []); [] -> {[], Buffer} end. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 835). -spec answer_request(browser_state(), integer(), gleam@dynamic:dynamic_()) -> list(pending_request()). answer_request(State, Id, Data) -> Found_request = gleam@list:pop_map( erlang:element(4, State), fun(Req) -> case erlang:element(2, Req) =:= Id of true -> {ok, Req}; false -> {error, nil} end end ), case Found_request of {ok, {Req@1, Rest}} -> gleam@erlang@process:send(erlang:element(3, Req@1), {ok, Data}), Rest; {error, nil} -> erlang:element(4, State) end. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 1012). -spec get_first_existing_path(list(binary())) -> {ok, binary()} | {error, launch_error()}. get_first_existing_path(Paths) -> Existing_paths = begin _pipe = Paths, gleam@list:filter( _pipe, fun(Current) -> case simplifile_erl:is_file(Current) of {ok, Res} -> Res; {error, _} -> false end end ) end, case Existing_paths of [First | _] -> {ok, First}; [] -> {error, could_not_find_executable} end. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 447). -spec get_system_chrome_path() -> {ok, binary()} | {error, launch_error()}. get_system_chrome_path() -> case gleam_erlang_ffi:os_family() of darwin -> get_first_existing_path( [<<"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"/utf8>>, <<"/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta"/utf8>>, <<"/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary"/utf8>>, <<"/Applications/Google Chrome Dev.app/Contents/MacOS/Google Chrome Dev"/utf8>>, <<"/Applications/Chromium.app/Contents/MacOS/Chromium"/utf8>>] ); linux -> get_first_existing_path( [<<"/opt/google/chrome/chrome"/utf8>>, <<"/opt/google/chrome-beta/chrome"/utf8>>, <<"/opt/google/chrome-unstable/chrome"/utf8>>, <<"/usr/bin/chromium"/utf8>>, <<"/usr/bin/chromium-browser"/utf8>>] ); windows_nt -> get_first_existing_path( [<<"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"/utf8>>, <<"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"/utf8>>, <<"C:\\Program Files\\Chromium\\Application\\chrome.exe"/utf8>>, <<"C:\\Program Files (x86)\\Chromium\\Application\\chrome.exe"/utf8>>] ); _ -> {error, unknow_operating_system} end. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 1057). -spec log_info(browser_state(), binary()) -> nil. log_info(State, Message) -> case erlang:element(8, State) of log_level_info -> gleam@io:println( <<<<<<"[INFO] "/utf8, (gleam@string:inspect(erlang:element(2, State)))/binary>>/binary, ": "/utf8>>/binary, Message/binary>> ); log_level_debug -> gleam@io:println( <<<<<<"[INFO] "/utf8, (gleam@string:inspect(erlang:element(2, State)))/binary>>/binary, ": "/utf8>>/binary, Message/binary>> ); _ -> nil end. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 1066). -spec log_warn(browser_state(), binary()) -> nil. log_warn(State, Message) -> case erlang:element(8, State) of log_level_info -> gleam@io:println( <<<<<<"[WARNING] "/utf8, (gleam@string:inspect(erlang:element(2, State)))/binary>>/binary, ": "/utf8>>/binary, Message/binary>> ); log_level_debug -> gleam@io:println( <<<<<<"[WARNING] "/utf8, (gleam@string:inspect(erlang:element(2, State)))/binary>>/binary, ": "/utf8>>/binary, Message/binary>> ); log_level_warnings -> gleam@io:println( <<<<<<"[WARNING] "/utf8, (gleam@string:inspect(erlang:element(2, State)))/binary>>/binary, ": "/utf8>>/binary, Message/binary>> ); _ -> nil end. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 864). -spec answer_failed_request(browser_state(), integer(), raw_browser_error()) -> list(pending_request()). answer_failed_request(State, Id, Data) -> Found_request = gleam@list:pop_map( erlang:element(4, State), fun(Req) -> case erlang:element(2, Req) =:= Id of true -> {ok, Req}; false -> {error, nil} end end ), case Found_request of {ok, {Req@1, Rest}} -> gleam@erlang@process:send( erlang:element(3, Req@1), {error, {browser_error, gleam@option:unwrap(erlang:element(2, Data), 0), gleam@option:unwrap( erlang:element(3, Data), <<"No message"/utf8>> ), gleam@option:unwrap( erlang:element(4, Data), <<"No data"/utf8>> )}} ), Rest; {error, nil} -> log_warn( State, <<"An error arrived from the browser but could not be associated with a request: "/utf8, (gleam@string:inspect(Data))/binary>> ), erlang:element(4, State) end. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 1079). -spec log_debug(browser_state(), fun(() -> binary())) -> nil. log_debug(State, Callback) -> case erlang:element(8, State) of log_level_debug -> gleam@io:println( <<<<<<"[DEBUG] "/utf8, (gleam@string:inspect(erlang:element(2, State)))/binary>>/binary, ": "/utf8>>/binary, (Callback())/binary>> ); _ -> nil end. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 923). -spec handle_port_response(browser_state(), binary()) -> browser_state(). handle_port_response(State, Response) -> Error_decoder = gleam@dynamic:decode3( fun(Field@0, Field@1, Field@2) -> {raw_browser_error, Field@0, Field@1, Field@2} end, gleam@dynamic:optional_field(<<"code"/utf8>>, fun gleam@dynamic:int/1), gleam@dynamic:optional_field( <<"message"/utf8>>, fun gleam@dynamic:string/1 ), gleam@dynamic:optional_field( <<"data"/utf8>>, fun gleam@dynamic:string/1 ) ), Response_decoder = gleam@dynamic:decode5( fun(Field@0, Field@1, Field@2, Field@3, Field@4) -> {browser_response, Field@0, Field@1, Field@2, Field@3, Field@4} end, gleam@dynamic:optional_field(<<"id"/utf8>>, fun gleam@dynamic:int/1), gleam@dynamic:optional_field( <<"result"/utf8>>, fun gleam@dynamic:dynamic/1 ), gleam@dynamic:optional_field( <<"method"/utf8>>, fun gleam@dynamic:string/1 ), gleam@dynamic:optional_field( <<"params"/utf8>>, fun gleam@dynamic:dynamic/1 ), gleam@dynamic:optional_field(<<"error"/utf8>>, Error_decoder) ), case gleam@json:decode(Response, Response_decoder) of {ok, {browser_response, {some, Id}, {some, Result}, none, none, none}} -> {browser_state, erlang:element(2, State), erlang:element(3, State), answer_request(State, Id, Result), erlang:element(5, State), erlang:element(6, State), erlang:element(7, State), erlang:element(8, State)}; {ok, {browser_response, {some, Id@1}, _, _, _, {some, Raw_error}}} -> {browser_state, erlang:element(2, State), erlang:element(3, State), answer_failed_request(State, Id@1, Raw_error), erlang:element(5, State), erlang:element(6, State), erlang:element(7, State), erlang:element(8, State)}; {ok, {browser_response, none, none, {some, Method}, {some, Params}, none}} -> gleam@list:each( erlang:element(5, State), fun(L) -> case erlang:element(1, L) =:= Method of true -> gleam@erlang@process:send( erlang:element(2, L), Params ); false -> log_debug( State, fun() -> <<"Ignored Event:"/utf8, (gleam@string:inspect(Params))/binary>> end ), nil end end ), State; {ok, _} -> log_warn( State, <<"Received an unexpectedly formatted response from the browser"/utf8>> ), gleam@io:debug(Response), State; {error, E} -> log_warn( State, <<"Failed to decode data from port message, ignoring! Resonse and error:"/utf8>> ), gleam@io:debug({Response, E}), State end. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 1090). -spec transform_call_response( {ok, {ok, XAM} | {error, request_error()}} | {error, gleam@erlang@process:call_error(any())} ) -> {ok, XAM} | {error, request_error()}. transform_call_response(Call_response) -> case Call_response of {error, {callee_down, _}} -> {error, chrome_agent_down}; {error, call_timeout} -> {error, chrome_agent_timeout}; {ok, Nested_result} -> Nested_result end. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 275). -spec call( gleam@erlang@process:subject(message()), binary(), gleam@option:option(gleam@json:json()), gleam@option:option(binary()), integer() ) -> {ok, gleam@dynamic:dynamic_()} | {error, request_error()}. call(Browser, Method, Params, Session_id, Time_out) -> _pipe = gleam@erlang@process:try_call( Browser, fun(_capture) -> {call, _capture, Method, Params, Session_id} end, Time_out ), transform_call_response(_pipe). -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 477). -spec create_init_fn(browser_config()) -> fun(() -> gleam@otp@actor:init_result(browser_state(), message())). create_init_fn(Cfg) -> fun() -> Cmd = erlang:element(2, Cfg), Args = [<<"--remote-debugging-pipe"/utf8>> | erlang:element(3, Cfg)], Res = chrobot_ffi:open_browser_port(Cmd, Args), case Res of {ok, Port} -> Instance = {browser_instance, Port}, Initial_state = {browser_state, Instance, 0, [], [], gleam@string_builder:new(), none, erlang:element(5, Cfg)}, log_info( Initial_state, <<"Port opened successfully, actor initialized"/utf8>> ), {ready, Initial_state, begin _pipe = gleam_erlang_ffi:new_selector(), gleam@erlang@process:selecting_record2( _pipe, Port, fun map_port_message/1 ) end}; {error, Err} -> chrobot@internal@utils:err(<<"Browser failed to start!"/utf8>>), gleam@io:debug(Err), {failed, <<"Browser did not start"/utf8>>} end end. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 1006). -spec send_to_browser(browser_state(), gleam@json:json()) -> {ok, gleam@dynamic:dynamic_()} | {error, gleam@dynamic:dynamic_()}. send_to_browser(State, Data) -> Payload = gleam@json:to_string(Data), log_debug(State, fun() -> <<"Sending Payload: "/utf8, Payload/binary>> end), chrobot_ffi:send_to_port( erlang:element(2, erlang:element(2, State)), <> ). -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 757). -spec handle_call( browser_state(), gleam@erlang@process:subject({ok, gleam@dynamic:dynamic_()} | {error, request_error()}), binary(), gleam@option:option(gleam@json:json()), gleam@option:option(binary()) ) -> gleam@otp@actor:next(any(), browser_state()). handle_call(State, Client, Method, Params, Session_id) -> Request_id = erlang:element(3, State), Request_memo = {pending_request, Request_id, Client}, Payload = gleam@json:object( begin _pipe = [{<<"id"/utf8>>, gleam@json:int(Request_id)}, {<<"method"/utf8>>, gleam@json:string(Method)}], _pipe@1 = chrobot@internal@utils:add_optional( _pipe, Params, fun(Some_params) -> {<<"params"/utf8>>, Some_params} end ), chrobot@internal@utils:add_optional( _pipe@1, Session_id, fun(Some_session_id) -> {<<"sessionId"/utf8>>, gleam@json:string(Some_session_id)} end ) end ), case send_to_browser(State, Payload) of {error, _} -> log_warn( State, <<"Request call to browser was unsuccessful!"/utf8>> ), gleam@erlang@process:send(Client, {error, port_error}), gleam@otp@actor:continue( {browser_state, erlang:element(2, State), Request_id + 1, erlang:element(4, State), erlang:element(5, State), erlang:element(6, State), erlang:element(7, State), erlang:element(8, State)} ); {ok, _} -> gleam@otp@actor:continue( {browser_state, erlang:element(2, State), Request_id + 1, [Request_memo | erlang:element(4, State)], erlang:element(5, State), erlang:element(6, State), erlang:element(7, State), erlang:element(8, State)} ) end. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 805). -spec handle_send( browser_state(), binary(), gleam@option:option(gleam@json:json()) ) -> gleam@otp@actor:next(any(), browser_state()). handle_send(State, Method, Params) -> Request_id = erlang:element(3, State), Payload = gleam@json:object( begin _pipe = [{<<"id"/utf8>>, gleam@json:int(Request_id)}, {<<"method"/utf8>>, gleam@json:string(Method)}], chrobot@internal@utils:add_optional( _pipe, Params, fun(Some_params) -> {<<"params"/utf8>>, Some_params} end ) end ), case send_to_browser(State, Payload) of {error, _} -> log_warn( State, <<"Request sent to browser was unsuccessful!"/utf8>> ), gleam@io:debug(Payload), nil; {ok, _} -> nil end, gleam@otp@actor:continue( {browser_state, erlang:element(2, State), Request_id + 1, erlang:element(4, State), erlang:element(5, State), erlang:element(6, State), erlang:element(7, State), erlang:element(8, State)} ). -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 627). -spec loop(message(), browser_state()) -> gleam@otp@actor:next(any(), browser_state()). loop(Message, State) -> case Message of kill -> log_warn( State, <<"Received kill signal, actor is shutting down, this is unuasual and means the browser did not respond to a shutdown request in time!"/utf8>> ), {stop, normal}; {call, Client, Method, Params, Session_id} -> handle_call(State, Client, Method, Params, Session_id); {send, Method@1, Params@1} -> handle_send(State, Method@1, Params@1); {add_listener, Client@1, Method@2} -> Updated_listeners = [{Method@2, Client@1} | erlang:element(5, State)], log_info( State, <<"Event listeners: "/utf8, (gleam@string:inspect(Updated_listeners))/binary>> ), gleam@otp@actor:continue( {browser_state, erlang:element(2, State), erlang:element(3, State), erlang:element(4, State), Updated_listeners, erlang:element(6, State), erlang:element(7, State), erlang:element(8, State)} ); {remove_listener, Client@2} -> Updated_listeners@1 = gleam@list:filter( erlang:element(5, State), fun(L) -> erlang:element(2, L) /= Client@2 end ), log_info( State, <<"Event listeners: "/utf8, (gleam@string:inspect(Updated_listeners@1))/binary>> ), gleam@otp@actor:continue( {browser_state, erlang:element(2, State), erlang:element(3, State), erlang:element(4, State), Updated_listeners@1, erlang:element(6, State), erlang:element(7, State), erlang:element(8, State)} ); {port_response, Data} -> {Chunks, Buffer} = process_port_message( Data, erlang:element(6, State) ), case string:is_empty(Buffer) of false -> log_info(State, <<"buffering browser message!"/utf8>>); true -> nil end, Updated_state = begin _pipe = Chunks, gleam@list:fold( _pipe, State, fun(Acc, Curr) -> handle_port_response(Acc, Curr) end ) end, gleam@otp@actor:continue( {browser_state, erlang:element(2, Updated_state), erlang:element(3, Updated_state), erlang:element(4, Updated_state), erlang:element(5, Updated_state), Buffer, erlang:element(7, Updated_state), erlang:element(8, State)} ); {unexpected_port_message, Msg} -> log_warn( State, <<"Got an unexpected message from the port! This should not happen!"/utf8>> ), gleam@io:debug(Msg), gleam@otp@actor:continue(State); {shutdown, Client@3} -> log_info( State, <<"Received shutdown request, attempting to quit browser"/utf8>> ), handle_send(State, <<"Browser.close"/utf8>>, none), gleam@otp@actor:continue( {browser_state, erlang:element(2, State), erlang:element(3, State), erlang:element(4, State), erlang:element(5, State), erlang:element(6, State), {some, Client@3}, erlang:element(8, State)} ); {port_exit, Exit_status} -> case erlang:element(7, State) of {some, Client@4} -> log_info( State, <<"Browser exited after shtudown request, actor is shutting down"/utf8>> ), gleam@erlang@process:send(Client@4, nil), {stop, normal}; _ -> log_warn( State, <<<<"Browser exited but there was no shutdown request! Exit Status: "/utf8, (gleam@string:inspect(Exit_status))/binary>>/binary, " browser actor is shutting down abnormally"/utf8>> ), {stop, {abnormal, <<"browser exited abnormally"/utf8>>}} end; {set_log_level, Level} -> gleam@otp@actor:continue( {browser_state, erlang:element(2, State), erlang:element(3, State), erlang:element(4, State), erlang:element(5, State), erlang:element(6, State), erlang:element(7, State), Level} ) end. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 127). -spec launch_with_config(browser_config()) -> {ok, gleam@erlang@process:subject(message())} | {error, launch_error()}. launch_with_config(Cfg) -> Launch_result = gleam@otp@actor:start_spec( {spec, create_init_fn(Cfg), erlang:element(4, Cfg), fun loop/2} ), case Launch_result of {ok, Browser} -> {ok, Browser}; {error, Err} -> gleam@io:println(<<"Failed to start browser"/utf8>>), gleam@io:debug(Err), {error, failed_to_start} end. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 267). -spec quit(gleam@erlang@process:subject(message())) -> {ok, nil} | {error, gleam@erlang@process:call_error(nil)}. quit(Browser) -> _ = gleam@erlang@process:send_after(Browser, 10000 * 2, kill), gleam@erlang@process:try_call( Browser, fun(_capture) -> {shutdown, _capture} end, 10000 ). -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 343). -spec get_version(gleam@erlang@process:subject(message())) -> {ok, browser_version()} | {error, request_error()}. get_version(Browser) -> gleam@result:'try'( call(Browser, <<"Browser.getVersion"/utf8>>, none, none, 10000), fun(Res) -> Version_decoder = gleam@dynamic:decode5( fun(Field@0, Field@1, Field@2, Field@3, Field@4) -> {browser_version, Field@0, Field@1, Field@2, Field@3, Field@4} end, gleam@dynamic:field( <<"protocolVersion"/utf8>>, fun gleam@dynamic:string/1 ), gleam@dynamic:field( <<"product"/utf8>>, fun gleam@dynamic:string/1 ), gleam@dynamic:field( <<"revision"/utf8>>, fun gleam@dynamic:string/1 ), gleam@dynamic:field( <<"userAgent"/utf8>>, fun gleam@dynamic:string/1 ), gleam@dynamic:field( <<"jsVersion"/utf8>>, fun gleam@dynamic:string/1 ) ), case Version_decoder(Res) of {ok, Version} -> {ok, Version}; {error, _} -> {error, protocol_error} end end ). -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 1029). -spec resolve_env_cofig() -> {ok, browser_config()} | {error, nil}. resolve_env_cofig() -> gleam@result:'try'( gleam_erlang_ffi:get_env(<<"CHROBOT_BROWSER_PATH"/utf8>>), fun(Path) -> Args = case gleam_erlang_ffi:get_env( <<"CHROBOT_BROWSER_ARGS"/utf8>> ) of {ok, Args_string} -> gleam@string:split(Args_string, <<"\n"/utf8>>); {error, nil} -> get_default_chrome_args() end, Time_out = case gleam_erlang_ffi:get_env( <<"CHROBOT_BROWSER_TIMEOUT"/utf8>> ) of {ok, Timeout_string} -> gleam@result:unwrap(gleam@int:parse(Timeout_string), 10000); {error, nil} -> 10000 end, Log_level = case gleam_erlang_ffi:get_env( <<"CHROBOT_LOG_LEVEL"/utf8>> ) of {ok, <<"silent"/utf8>>} -> log_level_silent; {ok, <<"warnings"/utf8>>} -> log_level_warnings; {ok, <<"info"/utf8>>} -> log_level_info; {ok, <<"debug"/utf8>>} -> log_level_debug; {ok, _} -> log_level_warnings; {error, nil} -> log_level_warnings end, {ok, {browser_config, Path, Args, Time_out, Log_level}} end ). -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 159). -spec launch() -> {ok, gleam@erlang@process:subject(message())} | {error, launch_error()}. launch() -> case resolve_env_cofig() of {ok, Env_config} -> chrobot@internal@utils:info( <<"Launching browser using config provided through environment variables"/utf8>> ), launch_with_config(Env_config); {error, _} -> gleam@result:'try'( gleam@result:lazy_or( get_local_chrome_path(), fun get_system_chrome_path/0 ), fun(Resolved_chrome_path) -> chrobot@internal@utils:info( <<<<"Launching browser from dynamically resolved path: \""/utf8, Resolved_chrome_path/binary>>/binary, "\""/utf8>> ), launch_with_config( {browser_config, Resolved_chrome_path, get_default_chrome_args(), 10000, log_level_warnings} ) end ) end. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 192). -spec launch_window() -> {ok, gleam@erlang@process:subject(message())} | {error, launch_error()}. launch_window() -> case resolve_env_cofig() of {ok, Env_config} -> chrobot@internal@utils:info( <<"Launching windowed browser using config provided through environment variables"/utf8>> ), launch_with_config( {browser_config, erlang:element(2, Env_config), begin _pipe = erlang:element(3, Env_config), gleam@list:filter(_pipe, fun(Arg) -> case Arg of <<"--headless"/utf8>> -> false; _ -> true end end) end, erlang:element(4, Env_config), erlang:element(5, Env_config)} ); {error, _} -> gleam@result:'try'( gleam@result:lazy_or( get_local_chrome_path(), fun get_system_chrome_path/0 ), fun(Resolved_chrome_path) -> chrobot@internal@utils:info( <<<<"Launching windowed browser from dynamically resolved path: \""/utf8, Resolved_chrome_path/binary>>/binary, "\""/utf8>> ), launch_with_config( {browser_config, Resolved_chrome_path, begin _pipe@1 = get_default_chrome_args(), gleam@list:filter( _pipe@1, fun(Arg@1) -> case Arg@1 of <<"--headless"/utf8>> -> false; _ -> true end end ) end, 10000, log_level_warnings} ) end ) end. -file("/Users/jonas/Projects/chrobot/src/chrobot/chrome.gleam", 251). -spec launch_with_env() -> {ok, gleam@erlang@process:subject(message())} | {error, launch_error()}. launch_with_env() -> case resolve_env_cofig() of {ok, Env_config} -> launch_with_config(Env_config); {error, _} -> chrobot@internal@utils:err( <<"Failed to resolve browser configuration from environment variables, please check that they are set correctly"/utf8>> ), {error, could_not_find_executable} end.