Current section
Files
Jump to
Current section
Files
src/chrobot@chrome.erl
-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@erlang@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)),
<<Payload/binary, "\x{0000}"/utf8>>
).
-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.