Add support for Epiphany web browser (#14852)

It's similar to the existing WebKit support, but a bit easier to use as
you can simply ./wpt run epiphany

Opportunistically improve a Servo error message, since we are adding
similar error messages below and consistency is nice.
diff --git a/tools/wpt/browser.py b/tools/wpt/browser.py
index 71d1e61..ea8cc04 100644
--- a/tools/wpt/browser.py
+++ b/tools/wpt/browser.py
@@ -758,3 +758,32 @@
 
     def version(self, binary=None, webdriver_binary=None):
         return None
+
+
+class Epiphany(Browser):
+    """Epiphany-specific interface."""
+
+    product = "epiphany"
+    requirements = "requirements_epiphany.txt"
+
+    def install(self, dest=None, channel=None):
+        raise NotImplementedError
+
+    def find_binary(self, venv_path=None, channel=None):
+        return find_executable("epiphany")
+
+    def find_webdriver(self, channel=None):
+        return find_executable("WebKitWebDriver")
+
+    def install_webdriver(self, dest=None, channel=None):
+        raise NotImplementedError
+
+    def version(self, binary=None, webdriver_binary=None):
+        if binary is None:
+            return None
+        output = call(binary, "--version")
+        if output:
+            # Stable release output looks like: "Web 3.30.2"
+            # Tech Preview output looks like "Web 3.31.3-88-g97db4f40f"
+            return output.split()[1]
+        return None
diff --git a/tools/wpt/run.py b/tools/wpt/run.py
index 44cf313..633d6b2 100644
--- a/tools/wpt/run.py
+++ b/tools/wpt/run.py
@@ -422,7 +422,7 @@
             binary = self.browser.find_binary(self.venv.path, None)
 
             if binary is None:
-                raise WptrunError("Unable to find servo binary on the PATH")
+                raise WptrunError("Unable to find servo binary in PATH")
             kwargs["binary"] = binary
 
 
@@ -442,6 +442,29 @@
         pass
 
 
+class Epiphany(BrowserSetup):
+    name = "epiphany"
+    browser_cls = browser.Epiphany
+
+    def install(self, venv, channel=None):
+        raise NotImplementedError
+
+    def setup_kwargs(self, kwargs):
+        if kwargs["binary"] is None:
+            binary = self.browser.find_binary()
+
+            if binary is None:
+                raise WptrunError("Unable to find epiphany in PATH")
+            kwargs["binary"] = binary
+
+        if kwargs["webdriver_binary"] is None:
+            webdriver_binary = self.browser.find_webdriver()
+
+            if webdriver_binary is None:
+                raise WptrunError("Unable to find WebKitWebDriver in PATH")
+            kwargs["webdriver_binary"] = webdriver_binary
+
+
 product_setup = {
     "fennec": Fennec,
     "firefox": Firefox,
@@ -456,6 +479,7 @@
     "sauce": Sauce,
     "opera": Opera,
     "webkit": WebKit,
+    "epiphany": Epiphany,
 }
 
 
diff --git a/tools/wptrunner/requirements_epiphany.txt b/tools/wptrunner/requirements_epiphany.txt
new file mode 100644
index 0000000..9115b7a
--- /dev/null
+++ b/tools/wptrunner/requirements_epiphany.txt
@@ -0,0 +1 @@
+mozprocess == 0.26
diff --git a/tools/wptrunner/wptrunner/browsers/__init__.py b/tools/wptrunner/wptrunner/browsers/__init__.py
index f86792d..fdedda4 100644
--- a/tools/wptrunner/wptrunner/browsers/__init__.py
+++ b/tools/wptrunner/wptrunner/browsers/__init__.py
@@ -34,4 +34,5 @@
                 "servo",
                 "servodriver",
                 "opera",
-                "webkit"]
+                "webkit",
+                "epiphany"]
diff --git a/tools/wptrunner/wptrunner/browsers/epiphany.py b/tools/wptrunner/wptrunner/browsers/epiphany.py
new file mode 100644
index 0000000..599ec9f
--- /dev/null
+++ b/tools/wptrunner/wptrunner/browsers/epiphany.py
@@ -0,0 +1,74 @@
+from .base import get_timeout_multiplier   # noqa: F401
+from .webkit import WebKitBrowser
+from ..executors import executor_kwargs as base_executor_kwargs
+from ..executors.executorwebdriver import (WebDriverTestharnessExecutor,  # noqa: F401
+                                           WebDriverRefTestExecutor)  # noqa: F401
+from ..executors.executorwebkit import WebKitDriverWdspecExecutor  # noqa: F401
+
+__wptrunner__ = {"product": "epiphany",
+                 "check_args": "check_args",
+                 "browser": "EpiphanyBrowser",
+                 "browser_kwargs": "browser_kwargs",
+                 "executor": {"testharness": "WebDriverTestharnessExecutor",
+                              "reftest": "WebDriverRefTestExecutor",
+                              "wdspec": "WebKitDriverWdspecExecutor"},
+                 "executor_kwargs": "executor_kwargs",
+                 "env_extras": "env_extras",
+                 "env_options": "env_options",
+                 "run_info_extras": "run_info_extras",
+                 "timeout_multiplier": "get_timeout_multiplier"}
+
+
+def check_args(**kwargs):
+    pass
+
+
+def browser_kwargs(test_type, run_info_data, config, **kwargs):
+    return {"binary": kwargs["binary"],
+            "webdriver_binary": kwargs["webdriver_binary"],
+            "webdriver_args": kwargs.get("webdriver_args")}
+
+
+def capabilities(server_config, **kwargs):
+    args = kwargs.get("binary_args", [])
+    if "--automation-mode" not in args:
+        args.append("--automation-mode")
+
+    return {
+        "browserName": "Epiphany",
+        "browserVersion": "3.31.4",  # First version to support automation
+        "platformName": "ANY",
+        "webkitgtk:browserOptions": {
+            "binary": kwargs["binary"],
+            "args": args,
+            "certificates": [
+                {"host": server_config["browser_host"],
+                 "certificateFile": kwargs["host_cert_path"]}]}}
+
+
+def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
+                    **kwargs):
+    executor_kwargs = base_executor_kwargs(test_type, server_config,
+                                           cache_manager, run_info_data, **kwargs)
+    executor_kwargs["close_after_done"] = True
+    executor_kwargs["capabilities"] = capabilities(server_config, **kwargs)
+    return executor_kwargs
+
+
+def env_extras(**kwargs):
+    return []
+
+
+def env_options():
+    return {}
+
+
+def run_info_extras(**kwargs):
+    return {"webkit_port": "gtk"}
+
+
+class EpiphanyBrowser(WebKitBrowser):
+    def __init__(self, logger, binary=None, webdriver_binary=None,
+                 webdriver_args=None):
+        WebKitBrowser.__init__(self, logger, binary, webdriver_binary,
+                               webdriver_args)