aboutsummaryrefslogtreecommitdiff
path: root/src/zen/browser_launcher.cpp
blob: a115bf46af1609d37a009d810b4ac1e02994f22f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// Copyright Epic Games, Inc. All Rights Reserved.

#include "browser_launcher.h"

#include <zenbase/zenbase.h>
#include <zencore/except_fmt.h>
#include <zencore/logging.h>

#include <stdexcept>
#include <string>

#if ZEN_PLATFORM_WINDOWS
#	include <zencore/windows.h>
#	include <shellapi.h>
#else
#	include <spawn.h>
#	include <sys/wait.h>
extern char** environ;
#endif

namespace zen {

void
LaunchBrowser(std::string_view Url)
{
	if (Url.empty())
	{
		throw zen::runtime_error("Cannot launch browser with empty URL");
	}

	bool Success = false;

#if ZEN_PLATFORM_WINDOWS
	std::string UrlZ(Url);
	HINSTANCE	Result = ShellExecuteA(nullptr, "open", UrlZ.c_str(), nullptr, nullptr, SW_SHOWNORMAL);
	Success			   = reinterpret_cast<intptr_t>(Result) > 32;
#else
#	if ZEN_PLATFORM_MAC
	const char* Program = "open";
#	elif ZEN_PLATFORM_LINUX
	const char* Program = "xdg-open";
#	else
	ZEN_NOT_IMPLEMENTED("Browser launching not implemented on this platform");
	const char* Program = nullptr;
#	endif

	// Spawn directly via posix_spawnp to avoid the shell entirely, so URL contents
	// cannot be interpreted as shell syntax regardless of what characters they contain.
	std::string Url_c(Url);
	char* const Argv[]	= {const_cast<char*>(Program), Url_c.data(), nullptr};
	pid_t		Pid		= 0;
	const int	SpawnRc = posix_spawnp(&Pid, Program, nullptr, nullptr, Argv, environ);
	if (SpawnRc == 0)
	{
		int Status = 0;
		if (waitpid(Pid, &Status, 0) == Pid)
		{
			Success = WIFEXITED(Status) && WEXITSTATUS(Status) == 0;
		}
	}
#endif

	if (!Success)
	{
		throw zen::runtime_error("Failed to launch browser for '{}'", Url);
	}

	ZEN_CONSOLE("Web browser launched for '{}' successfully", Url);
}

}  // namespace zen