Bug 1436459 [wpt PR 8614] - Remove duplicate copies of subdomains/hostnames, a=testonly
authorGeoffrey Sneddon <me@gsnedders.com>
Mon, 26 Mar 2018 11:17:50 +0000
changeset 466997 b0838962d2d707c00708f81ae4f80ddc161955a7
parent 466996 cc3255a9f8f4b107c6fd2e8a25c1bc357435281f
child 466998 8e8079c376b436cad4aa4c04efa003bc1e10ef04
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstestonly
bugs1436459
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1436459 [wpt PR 8614] - Remove duplicate copies of subdomains/hostnames, a=testonly Automatic update from web-platform-testsRemove dead code (wptrunner.hosts) -- Remove default host from browser env options -- Stop using server-locations.txt -- Remove duplicate copies of subdomains/hostnames wpt-commits: e1504e0c28e0dcb9323f881cb3582c7cb54b24a0, 1b4f253cc9fd6f63c7bb74d176969a15868c6af6, fef555b5fd43686e529ba612fbcfde05036e397c, 040654c78f3f021512a1f22b9b3b99925bd52d37 wpt-pr: 8614 wpt-commits: e1504e0c28e0dcb9323f881cb3582c7cb54b24a0, 1b4f253cc9fd6f63c7bb74d176969a15868c6af6, fef555b5fd43686e529ba612fbcfde05036e397c, 040654c78f3f021512a1f22b9b3b99925bd52d37 wpt-pr: 8614
testing/web-platform/meta/MANIFEST.json
testing/web-platform/tests/README.md
testing/web-platform/tests/docs/introduction.md
testing/web-platform/tests/tools/ci/commands.json
testing/web-platform/tests/tools/ci/lib.sh
testing/web-platform/tests/tools/ci/make_hosts_file.py
testing/web-platform/tests/tools/conftest.py
testing/web-platform/tests/tools/serve/serve.py
testing/web-platform/tests/tools/serve/test_serve.py
testing/web-platform/tests/tools/wpt/run.py
testing/web-platform/tests/tools/wptrunner/MANIFEST.in
testing/web-platform/tests/tools/wptrunner/setup.py
testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/chrome.py
testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/chrome_android.py
testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/edge.py
testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/firefox.py
testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/ie.py
testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/opera.py
testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/sauce.py
testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/server-locations.txt
testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/servodriver.py
testing/web-platform/tests/tools/wptrunner/wptrunner/environment.py
testing/web-platform/tests/tools/wptrunner/wptrunner/executors/executorservo.py
testing/web-platform/tests/tools/wptrunner/wptrunner/hosts.py
testing/web-platform/tests/tools/wptrunner/wptrunner/tests/browsers/__init__.py
testing/web-platform/tests/tools/wptrunner/wptrunner/tests/browsers/test_sauce.py
testing/web-platform/tests/tools/wptrunner/wptrunner/tests/test_hosts.py
testing/web-platform/tests/tools/wptrunner/wptrunner/wptrunner.py
testing/web-platform/tests/tools/wptserve/wptserve/pipes.py
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -386168,17 +386168,17 @@
    "cfedb92777a36954d6e285461bf224cb6d2b5407",
    "support"
   ],
   "./LICENSE.md": [
    "722729a1062b97ad2fdd43896b2c6a45b1fff144",
    "support"
   ],
   "./README.md": [
-   "c58b661f8a9377baf6d71b9e2b704441b127ef5d",
+   "8a00f4e0dea3810e28a32421ded06b8e78e94129",
    "support"
   ],
   "./check_stability.ini": [
    "f8af4287f3b0f6925a2a6c5c75b3788e24de1680",
    "support"
   ],
   "./config.default.json": [
    "403d365196f6fe2c631d27fe6042e3114d204016",
@@ -538308,17 +538308,17 @@
    "8a28f329253f3293cd8798d53672bd5a85c260ce",
    "support"
   ],
   "docs/index.html": [
    "12c7175a4d6c486ca9ebea4c6a43d2aec9845d8b",
    "support"
   ],
   "docs/introduction.md": [
-   "98606eeeb43d80624d6ad6fa51c741afedff1598",
+   "e7d0165e5c6a8604d25f32032b2374bff6e59e02",
    "support"
   ],
   "dom/OWNERS": [
    "2bfae729fb0eab4081f06e51ae5640b49e012b8e",
    "support"
   ],
   "dom/abort/event.any.js": [
    "d41904ddfc56e5ef3e89d965a4e5fa392e996ef9",
--- a/testing/web-platform/tests/README.md
+++ b/testing/web-platform/tests/README.md
@@ -27,27 +27,32 @@ Running the Tests
 The tests are designed to be run from your local computer. The test
 environment requires [Python 2.7+](http://www.python.org/downloads) (but not Python 3.x).
 
 On Windows, be sure to add the Python directory (`c:\python2x`, by default) to
 your `%Path%` [Environment Variable](http://www.computerhope.com/issues/ch000549.htm),
 and read the [Windows Notes](#windows-notes) section below.
 
 To get the tests running, you need to set up the test domains in your
-[`hosts` file](http://en.wikipedia.org/wiki/Hosts_%28file%29%23Location_in_the_file_system). The
-following entries are required:
+[`hosts` file](http://en.wikipedia.org/wiki/Hosts_%28file%29%23Location_in_the_file_system).
 
+The necessary content can be generated with `./wpt make-hosts-file`; on
+Windows, you will need to preceed the prior command with `python` or
+the path to the Python binary (`python wpt make-hosts-file`).
+
+For example, on most UNIX-like systems, you can setup the hosts file with:
+
+```bash
+./wpt make-hosts-file | sudo tee -a /etc/hosts
 ```
-127.0.0.1   web-platform.test
-127.0.0.1   www.web-platform.test
-127.0.0.1   www1.web-platform.test
-127.0.0.1   www2.web-platform.test
-127.0.0.1   xn--n8j6ds53lwwkrqhv28a.web-platform.test
-127.0.0.1   xn--lve-6lad.web-platform.test
-0.0.0.0     nonexistent-origin.web-platform.test
+
+And on Windows (note this requires an Administrator privileged shell):
+
+```bash
+python wpt make-hosts-file >> %SystemRoot%\System32\drivers\etc\hosts
 ```
 
 If you are behind a proxy, you also need to make sure the domains above are
 excluded from your proxy lookups.
 
 
 Running Tests Manually
 ======================
--- a/testing/web-platform/tests/docs/introduction.md
+++ b/testing/web-platform/tests/docs/introduction.md
@@ -97,27 +97,32 @@ anything as a result!
 The tests are designed to be run from your local computer. The test
 environment requires [Python 2.7+](http://www.python.org/downloads) (but not Python 3.x).
 
 On Windows, be sure to add the Python directory (`c:\python2x`, by default) to
 your `%Path%` [Environment Variable](http://www.computerhope.com/issues/ch000549.htm),
 and read the [Windows Notes](#windows-notes) section below.
 
 To get the tests running, you need to set up the test domains in your
-[`hosts` file](http://en.wikipedia.org/wiki/Hosts_%28file%29%23Location_in_the_file_system). The
-following entries are required:
+[`hosts` file](http://en.wikipedia.org/wiki/Hosts_%28file%29%23Location_in_the_file_system).
 
+The necessary content can be generated with `./wpt make-hosts-file`; on
+Windows, you will need to preceed the prior command with `python` or
+the path to the Python binary (`python wpt make-hosts-file`).
+
+For example, on most UNIX-like systems, you can setup the hosts file with:
+
+```bash
+./wpt make-hosts-file | sudo tee -a /etc/hosts
 ```
-127.0.0.1   web-platform.test
-127.0.0.1   www.web-platform.test
-127.0.0.1   www1.web-platform.test
-127.0.0.1   www2.web-platform.test
-127.0.0.1   xn--n8j6ds53lwwkrqhv28a.web-platform.test
-127.0.0.1   xn--lve-6lad.web-platform.test
-0.0.0.0     nonexistent-origin.web-platform.test
+
+And on Windows (note this requires an Administrator privileged shell):
+
+```bash
+python wpt make-hosts-file >> %SystemRoot%\System32\drivers\etc\hosts
 ```
 
 If you are behind a proxy, you also need to make sure the domains above are
 excluded from your proxy lookups.
 
 The test environment can then be started using
 
     ./wpt serve
--- a/testing/web-platform/tests/tools/ci/commands.json
+++ b/testing/web-platform/tests/tools/ci/commands.json
@@ -1,6 +1,7 @@
 {
     "test-jobs": {"path": "jobs.py", "script": "run", "parser": "create_parser", "help": "List test jobs that should run for a set of commits",
                   "virtualenv": false},
     "check-stability": {"path": "check_stability.py", "script": "run", "parser": "get_parser", "parse_known": true, "help": "Check test stability",
-                        "virtualenv": true, "install": ["requests"], "requirements": ["../wptrunner/requirements.txt"]}
+                        "virtualenv": true, "install": ["requests"], "requirements": ["../wptrunner/requirements.txt"]},
+  "make-hosts-file": {"path": "make_hosts_file.py", "script": "run", "parser": "create_parser", "help": "Output a hosts file to stdout", "virtualenv": false}
 }
--- a/testing/web-platform/tests/tools/ci/lib.sh
+++ b/testing/web-platform/tests/tools/ci/lib.sh
@@ -1,25 +1,17 @@
 #!/bin/bash
 
 hosts_fixup() {
     echo "travis_fold:start:hosts_fixup"
     echo "Rewriting hosts file"
     echo "## /etc/hosts ##"
     cat /etc/hosts
     sudo sed -i 's/^::1\s*localhost/::1/' /etc/hosts
-    sudo sh -c 'echo "
-127.0.0.1 web-platform.test
-127.0.0.1 www.web-platform.test
-127.0.0.1 www1.web-platform.test
-127.0.0.1 www2.web-platform.test
-127.0.0.1 xn--n8j6ds53lwwkrqhv28a.web-platform.test
-127.0.0.1 xn--lve-6lad.web-platform.test
-0.0.0.0 nonexistent-origin.web-platform.test
-" >> /etc/hosts'
+    ./wpt make-hosts-file | sudo tee -a /etc/hosts
     echo "== /etc/hosts =="
     cat /etc/hosts
     echo "----------------"
     echo "travis_fold:end:hosts_fixup"
 }
 
 install_chrome() {
     channel=$1
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/ci/make_hosts_file.py
@@ -0,0 +1,19 @@
+import argparse
+import os
+
+from ..localpaths import repo_root
+
+from ..serve.serve import load_config, normalise_config, make_hosts_file
+
+def create_parser():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("address", default="127.0.0.1", nargs="?", help="Address that hosts should point at")
+    return parser
+
+def run(**kwargs):
+    config = load_config(os.path.join(repo_root, "config.default.json"),
+                         os.path.join(repo_root, "config.json"))
+
+    config = normalise_config(config, {})
+
+    print(make_hosts_file(config, kwargs["address"]))
--- a/testing/web-platform/tests/tools/conftest.py
+++ b/testing/web-platform/tests/tools/conftest.py
@@ -1,13 +1,22 @@
 import platform
 import os
+import sys
 
 from hypothesis import settings, HealthCheck
 
 impl = platform.python_implementation()
 
 settings.register_profile("ci", settings(max_examples=1000,
                                          suppress_health_check=[HealthCheck.too_slow]))
 settings.register_profile("pypy", settings(suppress_health_check=[HealthCheck.too_slow]))
 
 settings.load_profile(os.getenv("HYPOTHESIS_PROFILE",
                                 "default" if impl != "PyPy" else "pypy"))
+
+# serve can't even be imported on Py3, so totally ignore it even from collection
+collect_ignore = []
+if sys.version_info[0] >= 3:
+    serve = os.path.join(os.path.dirname(__file__), "serve")
+    collect_ignore.extend([os.path.join(root, f)
+                           for root, _, files in os.walk(serve)
+                           for f in files])
--- a/testing/web-platform/tests/tools/serve/serve.py
+++ b/testing/web-platform/tests/tools/serve/serve.py
@@ -198,16 +198,18 @@ done();
 rewrites = [("GET", "/resources/WebIDLParser.js", "/resources/webidl2/lib/webidl2.js")]
 
 subdomains = [u"www",
               u"www1",
               u"www2",
               u"天気の良い日",
               u"élève"]
 
+not_subdomains = [u"nonexistent-origin"]
+
 class RoutesBuilder(object):
     def __init__(self):
         self.forbidden_override = [("GET", "/tools/runner/*", handlers.file_handler),
                                    ("POST", "/tools/runner/update_manifest.py",
                                     handlers.python_script_handler)]
 
         self.forbidden = [("*", "/_certs/*", handlers.ErrorHandler(404)),
                           ("*", "/tools/*", handlers.ErrorHandler(404)),
@@ -463,16 +465,34 @@ def check_subdomains(host, paths, bind_h
 
 
 def get_subdomains(host):
     #This assumes that the tld is ascii-only or already in punycode
     return {subdomain: (subdomain.encode("idna"), host)
             for subdomain in subdomains}
 
 
+def get_not_subdomains(host):
+    #This assumes that the tld is ascii-only or already in punycode
+    return {subdomain: (subdomain.encode("idna"), host)
+            for subdomain in not_subdomains}
+
+
+def make_hosts_file(config, host):
+    rv = []
+
+    for domain in config["domains"].values():
+        rv.append("%s\t%s\n" % (host, domain))
+
+    for not_domain in config.get("not_domains", {}).values():
+        rv.append("0.0.0.0\t%s\n" % not_domain)
+
+    return "".join(rv)
+
+
 def start_servers(host, ports, paths, routes, bind_hostname, config, ssl_config,
                   **kwargs):
     servers = defaultdict(list)
     for scheme, ports in ports.iteritems():
         assert len(ports) == {"http":2}.get(scheme, 1)
 
         for port in ports:
             if port is None:
@@ -621,34 +641,39 @@ def get_ports(config, ssl_environment):
             rv[scheme].append(port)
     return rv
 
 
 
 def normalise_config(config, ports):
     host = config["external_host"] if config["external_host"] else config["host"]
     domains = get_subdomains(host)
+    not_domains = get_not_subdomains(host)
     ports_ = {}
     for scheme, ports_used in ports.iteritems():
         ports_[scheme] = ports_used
 
     for key, value in domains.iteritems():
         domains[key] = ".".join(value)
 
+    for key, value in not_domains.iteritems():
+        not_domains[key] = ".".join(value)
+
     domains[""] = host
 
     ports_ = {}
     for scheme, ports_used in ports.iteritems():
         ports_[scheme] = ports_used
 
     # make a (shallow) copy of the config and update that, so that the
     # normalized config can be used in place of the original one.
     config_ = config.copy()
     config_["host"] = host
     config_["domains"] = domains
+    config_["not_domains"] = not_domains
     config_["ports"] = ports_
     return config_
 
 
 def get_paths(config):
     return {"doc_root": config["doc_root"],
             "ws_doc_root": config["ws_doc_root"]}
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/tools/serve/test_serve.py
@@ -0,0 +1,12 @@
+from . import serve
+
+def test_make_hosts_file():
+    hosts = serve.make_hosts_file({
+        "domains": {"www": "www.foo.bar.test", "www1": "www1.foo.bar.test"},
+        "not_domains": {"aaa": "aaa.foo.bar.test", "bbb": "bbb.foo.bar.test"}
+    }, "127.1.1.1")
+    lines = hosts.split("\n")
+    assert "127.1.1.1\twww.foo.bar.test" in lines
+    assert "127.1.1.1\twww1.foo.bar.test" in lines
+    assert "0.0.0.0\taaa.foo.bar.test" in lines
+    assert "0.0.0.0\tbbb.foo.bar.test" in lines
--- a/testing/web-platform/tests/tools/wpt/run.py
+++ b/testing/web-platform/tests/tools/wpt/run.py
@@ -6,16 +6,18 @@ import subprocess
 import sys
 import tarfile
 from distutils.spawn import find_executable
 
 wpt_root = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))
 sys.path.insert(0, os.path.abspath(os.path.join(wpt_root, "tools")))
 
 from . import browser, utils, virtualenv
+from ..serve import serve
+
 logger = None
 
 
 class WptrunError(Exception):
     pass
 
 
 class WptrunnerHelpAction(argparse.Action):
@@ -89,43 +91,44 @@ your PATH.
 Otherwise run with --ssl-type=none""")
             else:
                 raise WptrunError("""OpenSSL not found. If you don't need HTTPS support run with --ssl-type=none,
 otherwise install OpenSSL and ensure that it's on your $PATH.""")
 
 
 def check_environ(product):
     if product not in ("firefox", "servo"):
-        expected_hosts = ["web-platform.test",
-                          "www.web-platform.test",
-                          "www1.web-platform.test",
-                          "www2.web-platform.test",
-                          "xn--n8j6ds53lwwkrqhv28a.web-platform.test",
-                          "xn--lve-6lad.web-platform.test",
-                          "nonexistent-origin.web-platform.test"]
+        expected_hosts = {".".join(x)
+                          for x in serve.get_subdomains("web-platform.test").values()}
+        expected_hosts |= {".".join(x)
+                           for x in serve.get_not_subdomains("web-platform.test").values()}
         missing_hosts = set(expected_hosts)
         if platform.uname()[0] != "Windows":
             hosts_path = "/etc/hosts"
         else:
             hosts_path = "C:\Windows\System32\drivers\etc\hosts"
         with open(hosts_path, "r") as f:
             for line in f:
                 line = line.split("#", 1)[0].strip()
                 parts = line.split()
                 hosts = parts[1:]
                 for host in hosts:
                     missing_hosts.discard(host)
             if missing_hosts:
-                raise WptrunError("""Missing hosts file configuration. Expected entries like:
+                if platform.uname()[0] != "Windows":
+                    message = """Missing hosts file configuration. Run
 
-%s
+python wpt make-hosts-file >> %s
 
-See README.md for more details.""" % "\n".join("%s\t%s" %
-                                               ("127.0.0.1" if "nonexistent" not in host else "0.0.0.0", host)
-                                               for host in expected_hosts))
+from a shell with Administrator privileges.""" % hosts_path
+                else:
+                    message = """Missing hosts file configuration. Run
+
+./wpt make-hosts-file | sudo tee -a %s""" % hosts_path
+                raise WptrunError(message)
 
 
 class BrowserSetup(object):
     name = None
     browser_cls = None
 
     def __init__(self, venv, prompt=True, sub_product=None):
         self.browser = self.browser_cls()
--- a/testing/web-platform/tests/tools/wptrunner/MANIFEST.in
+++ b/testing/web-platform/tests/tools/wptrunner/MANIFEST.in
@@ -1,8 +1,7 @@
 exclude MANIFEST.in
 include requirements.txt
 include wptrunner.default.ini
 include wptrunner/testharness_runner.html
 include wptrunner/*.js
 include wptrunner/executors/*.js
 include wptrunner/config.json
-include wptrunner/browsers/server-locations.txt
\ No newline at end of file
--- a/testing/web-platform/tests/tools/wptrunner/setup.py
+++ b/testing/web-platform/tests/tools/wptrunner/setup.py
@@ -47,17 +47,16 @@ setup(name=PACKAGE_NAME,
       package_data={"wptrunner": ["executors/testharness_marionette.js",
                                   "executors/testharness_webdriver.js",
                                   "executors/reftest.js",
                                   "executors/reftest-wait.js",
                                   "testharnessreport.js",
                                   "testharness_runner.html",
                                   "config.json",
                                   "wptrunner.default.ini",
-                                  "browsers/server-locations.txt",
                                   "browsers/sauce_setup/*",
                                   "prefs/*"]},
       include_package_data=True,
       data_files=[("requirements", requirements_files)],
       install_requires=deps
       )
 
 if "install" in sys.argv:
--- a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/chrome.py
+++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/chrome.py
@@ -55,18 +55,17 @@ def executor_kwargs(test_type, server_co
     return executor_kwargs
 
 
 def env_extras(**kwargs):
     return []
 
 
 def env_options():
-    return {"host": "web-platform.test",
-            "bind_hostname": "true"}
+    return {"bind_hostname": "true"}
 
 
 class ChromeBrowser(Browser):
     """Chrome is backed by chromedriver, which is supplied through
     ``wptrunner.webdriver.ChromeDriverServer``.
     """
 
     def __init__(self, logger, binary, webdriver_binary="chromedriver",
--- a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/chrome_android.py
+++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/chrome_android.py
@@ -63,18 +63,17 @@ def executor_kwargs(test_type, server_co
     return executor_kwargs
 
 
 def env_extras(**kwargs):
     return []
 
 
 def env_options():
-    return {"host": "web-platform.test",
-            "bind_hostname": "true"}
+    return {"bind_hostname": "true"}
 
 
 class ChromeAndroidBrowser(Browser):
     """Chrome is backed by chromedriver, which is supplied through
     ``wptrunner.webdriver.ChromeDriverServer``.
     """
 
     def __init__(self, logger, binary, webdriver_binary="chromedriver",
--- a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/edge.py
+++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/edge.py
@@ -33,18 +33,17 @@ def executor_kwargs(test_type, server_co
     executor_kwargs["close_after_done"] = True
     executor_kwargs["capabilities"] = dict(DesiredCapabilities.EDGE.items())
     return executor_kwargs
 
 def env_extras(**kwargs):
     return []
 
 def env_options():
-    return {"host": "web-platform.test",
-            "bind_hostname": "true",
+    return {"bind_hostname": "true",
             "supports_debugger": False}
 
 class EdgeBrowser(Browser):
     used_ports = set()
 
     def __init__(self, logger, webdriver_binary, webdriver_args=None):
         Browser.__init__(self, logger)
         self.server = EdgeDriverServer(self.logger,
--- a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/firefox.py
+++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/firefox.py
@@ -1,34 +1,33 @@
 import os
 import platform
 import signal
 import subprocess
 import sys
+import tempfile
 
 import mozinfo
 import mozleak
 from mozprocess import ProcessHandler
 from mozprofile import FirefoxProfile, Preferences
-from mozprofile.permissions import ServerLocations
 from mozrunner import FirefoxRunner
 from mozrunner.utils import get_stack_fixer_function
 from mozcrash import mozcrash
 
 from .base import (get_free_port,
                    Browser,
                    ExecutorBrowser,
                    require_arg,
                    cmd_arg,
                    browser_command)
 from ..executors import executor_kwargs as base_executor_kwargs
 from ..executors.executormarionette import (MarionetteTestharnessExecutor,
                                             MarionetteRefTestExecutor,
                                             MarionetteWdspecExecutor)
-from ..environment import hostnames
 
 
 here = os.path.join(os.path.split(__file__)[0])
 
 __wptrunner__ = {"product": "firefox",
                  "check_args": "check_args",
                  "browser": "FirefoxBrowser",
                  "executor": {"testharness": "MarionetteTestharnessExecutor",
@@ -75,17 +74,18 @@ def browser_kwargs(test_type, run_info_d
             "e10s": kwargs["gecko_e10s"],
             "stackfix_dir": kwargs["stackfix_dir"],
             "binary_args": kwargs["binary_args"],
             "timeout_multiplier": get_timeout_multiplier(test_type,
                                                          run_info_data,
                                                          **kwargs),
             "leak_check": kwargs["leak_check"],
             "stylo_threads": kwargs["stylo_threads"],
-            "chaos_mode_flags": kwargs["chaos_mode_flags"]}
+            "chaos_mode_flags": kwargs["chaos_mode_flags"],
+            "config": kwargs["config"]}
 
 
 def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
                     **kwargs):
     executor_kwargs = base_executor_kwargs(test_type, server_config,
                                            cache_manager, **kwargs)
     executor_kwargs["close_after_done"] = test_type != "reftest"
     executor_kwargs["timeout_multiplier"] = get_timeout_multiplier(test_type,
@@ -97,17 +97,17 @@ def executor_kwargs(test_type, server_co
         executor_kwargs["reftest_screenshot"] = kwargs["reftest_screenshot"]
     if test_type == "wdspec":
         options = {}
         if kwargs["binary"]:
             options["binary"] = kwargs["binary"]
         if kwargs["binary_args"]:
             options["args"] = kwargs["binary_args"]
         options["prefs"] = {
-            "network.dns.localDomains": ",".join(hostnames)
+            "network.dns.localDomains": ",".join(server_config['domains'].values())
         }
         capabilities["moz:firefoxOptions"] = options
     if kwargs["certutil_binary"] is None:
         capabilities["acceptInsecureCerts"] = True
     if capabilities:
         executor_kwargs["capabilities"] = capabilities
     return executor_kwargs
 
@@ -138,32 +138,33 @@ class FirefoxBrowser(Browser):
     used_ports = set()
     init_timeout = 60
     shutdown_timeout = 60
 
     def __init__(self, logger, binary, prefs_root, test_type, extra_prefs=None, debug_info=None,
                  symbols_path=None, stackwalk_binary=None, certutil_binary=None,
                  ca_certificate_path=None, e10s=False, stackfix_dir=None,
                  binary_args=None, timeout_multiplier=None, leak_check=False, stylo_threads=1,
-                 chaos_mode_flags=None):
+                 chaos_mode_flags=None, config=None):
         Browser.__init__(self, logger)
         self.binary = binary
         self.prefs_root = prefs_root
         self.test_type = test_type
         self.extra_prefs = extra_prefs
         self.marionette_port = None
         self.runner = None
         self.debug_info = debug_info
         self.profile = None
         self.symbols_path = symbols_path
         self.stackwalk_binary = stackwalk_binary
         self.ca_certificate_path = ca_certificate_path
         self.certutil_binary = certutil_binary
         self.e10s = e10s
         self.binary_args = binary_args
+        self.config = config
         if stackfix_dir:
             self.stack_fixer = get_stack_fixer_function(stackfix_dir,
                                                         self.symbols_path)
         else:
             self.stack_fixer = None
 
         if timeout_multiplier:
             self.init_timeout = self.init_timeout * timeout_multiplier
@@ -184,25 +185,22 @@ class FirefoxBrowser(Browser):
         env = os.environ.copy()
         env["MOZ_CRASHREPORTER"] = "1"
         env["MOZ_CRASHREPORTER_SHUTDOWN"] = "1"
         env["MOZ_DISABLE_NONLOCAL_CONNECTIONS"] = "1"
         env["STYLO_THREADS"] = str(self.stylo_threads)
         if self.chaos_mode_flags is not None:
             env["MOZ_CHAOSMODE"] = str(self.chaos_mode_flags)
 
-        locations = ServerLocations(filename=os.path.join(here, "server-locations.txt"))
-
         preferences = self.load_prefs()
 
-        self.profile = FirefoxProfile(locations=locations,
-                                      preferences=preferences)
+        self.profile = FirefoxProfile(preferences=preferences)
         self.profile.set_preferences({"marionette.port": self.marionette_port,
                                       "dom.disable_open_during_load": False,
-                                      "network.dns.localDomains": ",".join(hostnames),
+                                      "network.dns.localDomains": ",".join(self.config['domains'].values()),
                                       "network.proxy.type": 0,
                                       "places.history.enabled": False,
                                       "dom.send_after_paint_to_content": True,
                                       "network.preload": True})
         if self.e10s:
             self.profile.set_preferences({"browser.tabs.remote.autostart": True})
 
         if self.test_type == "reftest":
--- a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/ie.py
+++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/ie.py
@@ -37,18 +37,17 @@ def executor_kwargs(test_type, server_co
     executor_kwargs["close_after_done"] = True
     executor_kwargs["capabilities"] = capabilities
     return executor_kwargs
 
 def env_extras(**kwargs):
     return []
 
 def env_options():
-    return {"host": "web-platform.test",
-            "bind_hostname": "true",
+    return {"bind_hostname": "true",
             "supports_debugger": False}
 
 class InternetExplorerBrowser(Browser):
     used_ports = set()
 
     def __init__(self, logger, webdriver_binary, webdriver_args=None):
         Browser.__init__(self, logger)
         self.server = InternetExplorerDriverServer(self.logger,
--- a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/opera.py
+++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/opera.py
@@ -55,18 +55,17 @@ def executor_kwargs(test_type, server_co
     return executor_kwargs
 
 
 def env_extras(**kwargs):
     return []
 
 
 def env_options():
-    return {"host": "web-platform.test",
-            "bind_hostname": "true"}
+    return {"bind_hostname": "true"}
 
 
 class OperaBrowser(Browser):
     """Opera is backed by operadriver, which is supplied through
     ``wptrunner.webdriver.OperaDriverServer``.
     """
 
     def __init__(self, logger, binary, webdriver_binary="operadriver",
--- a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/sauce.py
+++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/sauce.py
@@ -108,31 +108,31 @@ def executor_kwargs(test_type, server_co
     return executor_kwargs
 
 
 def env_extras(**kwargs):
     return [SauceConnect(**kwargs)]
 
 
 def env_options():
-    return {"host": "web-platform.test",
-            "bind_hostname": "true",
+    return {"bind_hostname": "true",
             "supports_debugger": False}
 
 
 def get_tar(url, dest):
     resp = requests.get(url, stream=True)
     resp.raise_for_status()
     with tarfile.open(fileobj=CStringIO(resp.raw.read())) as f:
         f.extractall(path=dest)
 
 
 class SauceConnect():
 
     def __init__(self, **kwargs):
+        self.config = kwargs["config"]
         self.sauce_user = kwargs["sauce_user"]
         self.sauce_key = kwargs["sauce_key"]
         self.sauce_tunnel_id = kwargs["sauce_tunnel_id"]
         self.sauce_connect_binary = kwargs.get("sauce_connect_binary")
         self.sc_process = None
         self.temp_dir = None
 
     def __enter__(self, options):
@@ -148,18 +148,17 @@ class SauceConnect():
             self.sauce_connect_binary,
             "--user=%s" % self.sauce_user,
             "--api-key=%s" % self.sauce_key,
             "--no-remove-colliding-tunnels",
             "--tunnel-identifier=%s" % self.sauce_tunnel_id,
             "--metrics-address=0.0.0.0:9876",
             "--readyfile=./sauce_is_ready",
             "--tunnel-domains",
-            "web-platform.test",
-            "*.web-platform.test"
+            ",".join(self.config['domains'].values())
         ])
 
         # Timeout config vars
         each_sleep_secs = 1
         max_wait = 30
         kill_wait = 5
 
         tot_wait = 0
deleted file mode 100644
--- a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/server-locations.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-http://localhost:8000    primary
-
-http://web-platform.test:8000
-http://www.web-platform.test:8000
-http://www1.web-platform.test:8000
-http://www2.web-platform.test:8000
-http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8000
-http://xn--lve-6lad.web-platform.test:8000
-
-http://web-platform.test:8001
-http://www.web-platform.test:8001
-http://www1.web-platform.test:8001
-http://www2.web-platform.test:8001
-http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8001
-http://xn--lve-6lad.web-platform.test:8001
-
-https://web-platform.test:8443
-https://www.web-platform.test:8443
-https://www1.web-platform.test:8443
-https://www2.web-platform.test:8443
-https://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8443
-https://xn--lve-6lad.web-platform.test:8443
-
-# These are actually ws servers, but until mozprofile is
-# fixed we have to pretend that they are http servers
-http://web-platform.test:8888
-http://www.web-platform.test:8888
-http://www1.web-platform.test:8888
-http://www2.web-platform.test:8888
-http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8888
-http://xn--lve-6lad.web-platform.test:8888
--- a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/servodriver.py
+++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/servodriver.py
@@ -1,14 +1,17 @@
 import os
+import shutil
 import subprocess
 import tempfile
 
 from mozprocess import ProcessHandler
 
+from serve.serve import make_hosts_file
+
 from .base import Browser, require_arg, get_free_port, browser_command, ExecutorBrowser
 from ..executors import executor_kwargs as base_executor_kwargs
 from ..executors.executorservodriver import (ServoWebDriverTestharnessExecutor,
                                              ServoWebDriverRefTestExecutor)
 
 here = os.path.join(os.path.split(__file__)[0])
 
 __wptrunner__ = {
@@ -21,24 +24,16 @@ here = os.path.join(os.path.split(__file
     },
     "browser_kwargs": "browser_kwargs",
     "executor_kwargs": "executor_kwargs",
     "env_extras": "env_extras",
     "env_options": "env_options",
     "update_properties": "update_properties",
 }
 
-hosts_text = """127.0.0.1 web-platform.test
-127.0.0.1 www.web-platform.test
-127.0.0.1 www1.web-platform.test
-127.0.0.1 www2.web-platform.test
-127.0.0.1 xn--n8j6ds53lwwkrqhv28a.web-platform.test
-127.0.0.1 xn--lve-6lad.web-platform.test
-"""
-
 
 def check_args(**kwargs):
     require_arg(kwargs, "binary")
 
 
 def browser_kwargs(test_type, run_info_data, **kwargs):
     return {
         "binary": kwargs["binary"],
@@ -64,35 +59,35 @@ def env_options():
             "testharnessreport": "testharnessreport-servodriver.js",
             "supports_debugger": True}
 
 
 def update_properties():
     return ["debug", "os", "version", "processor", "bits"], None
 
 
-def make_hosts_file():
+def write_hosts_file(config):
     hosts_fd, hosts_path = tempfile.mkstemp()
     with os.fdopen(hosts_fd, "w") as f:
-        f.write(hosts_text)
+        f.write(make_hosts_file(config, "127.0.0.1"))
     return hosts_path
 
 
 class ServoWebDriverBrowser(Browser):
     used_ports = set()
 
     def __init__(self, logger, binary, debug_info=None, webdriver_host="127.0.0.1",
                  user_stylesheets=None):
         Browser.__init__(self, logger)
         self.binary = binary
         self.webdriver_host = webdriver_host
         self.webdriver_port = None
         self.proc = None
         self.debug_info = debug_info
-        self.hosts_path = make_hosts_file()
+        self.hosts_path = write_hosts_file()
         self.command = None
         self.user_stylesheets = user_stylesheets if user_stylesheets else []
 
     def start(self, **kwargs):
         self.webdriver_port = get_free_port(4444, exclude=self.used_ports)
         self.used_ports.add(self.webdriver_port)
 
         env = os.environ.copy()
@@ -153,13 +148,14 @@ class ServoWebDriverBrowser(Browser):
 
     def is_alive(self):
         if self.runner:
             return self.runner.is_running()
         return False
 
     def cleanup(self):
         self.stop()
+        shutil.rmtree(os.path.dirname(self.hosts_file))
 
     def executor_browser(self):
         assert self.webdriver_port is not None
         return ExecutorBrowser, {"webdriver_host": self.webdriver_host,
                                  "webdriver_port": self.webdriver_port}
--- a/testing/web-platform/tests/tools/wptrunner/wptrunner/environment.py
+++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/environment.py
@@ -13,24 +13,16 @@ from wptserve.handlers import StringHand
 
 here = os.path.split(__file__)[0]
 repo_root = os.path.abspath(os.path.join(here, os.pardir, os.pardir, os.pardir))
 
 serve = None
 sslutils = None
 
 
-hostnames = ["web-platform.test",
-             "www.web-platform.test",
-             "www1.web-platform.test",
-             "www2.web-platform.test",
-             "xn--n8j6ds53lwwkrqhv28a.web-platform.test",
-             "xn--lve-6lad.web-platform.test"]
-
-
 def do_delayed_imports(logger, test_paths):
     global serve, sslutils
 
     serve_root = serve_path(test_paths)
     sys.path.insert(0, serve_root)
 
     failed = []
 
--- a/testing/web-platform/tests/tools/wptrunner/wptrunner/executors/executorservo.py
+++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/executors/executorservo.py
@@ -8,16 +8,18 @@ import tempfile
 import threading
 import traceback
 import urlparse
 import uuid
 from collections import defaultdict
 
 from mozprocess import ProcessHandler
 
+from serve.serve import make_hosts_file
+
 from .base import (ExecutorException,
                    Protocol,
                    RefTestImplementation,
                    testharness_result_converter,
                    reftest_result_converter,
                    WdspecExecutor, WebDriverProtocol)
 from .process import ProcessTestExecutor
 from ..browsers.base import browser_command
@@ -25,44 +27,36 @@ from ..wpttest import WdspecResult, Wdsp
 from ..webdriver_server import ServoDriverServer
 from .executormarionette import WdspecRun
 
 pytestrunner = None
 webdriver = None
 
 extra_timeout = 5  # seconds
 
-hosts_text = """127.0.0.1 web-platform.test
-127.0.0.1 www.web-platform.test
-127.0.0.1 www1.web-platform.test
-127.0.0.1 www2.web-platform.test
-127.0.0.1 xn--n8j6ds53lwwkrqhv28a.web-platform.test
-127.0.0.1 xn--lve-6lad.web-platform.test
-"""
-
-def make_hosts_file():
+def write_hosts_file(config):
     hosts_fd, hosts_path = tempfile.mkstemp()
     with os.fdopen(hosts_fd, "w") as f:
-        f.write(hosts_text)
+        f.write(make_hosts_file(config, "127.0.0.1"))
     return hosts_path
 
 
 class ServoTestharnessExecutor(ProcessTestExecutor):
     convert_result = testharness_result_converter
 
     def __init__(self, browser, server_config, timeout_multiplier=1, debug_info=None,
                  pause_after_test=False, **kwargs):
         ProcessTestExecutor.__init__(self, browser, server_config,
                                      timeout_multiplier=timeout_multiplier,
                                      debug_info=debug_info)
         self.pause_after_test = pause_after_test
         self.result_data = None
         self.result_flag = None
         self.protocol = Protocol(self, browser)
-        self.hosts_path = make_hosts_file()
+        self.hosts_path = write_hosts_file(server_config)
 
     def teardown(self):
         try:
             os.unlink(self.hosts_path)
         except OSError:
             pass
         ProcessTestExecutor.teardown(self)
 
deleted file mode 100644
--- a/testing/web-platform/tests/tools/wptrunner/wptrunner/hosts.py
+++ /dev/null
@@ -1,100 +0,0 @@
-from __future__ import unicode_literals
-
-
-class HostsLine(object):
-    def __init__(self, ip_address, canonical_hostname, aliases=None, comment=None):
-        self.ip_address = ip_address
-        self.canonical_hostname = canonical_hostname
-        self.aliases = aliases if aliases is not None else []
-        self.comment = comment
-        if self.ip_address is None:
-            assert self.canonical_hostname is None
-            assert not self.aliases
-            assert self.comment is not None
-
-    @classmethod
-    def from_string(cls, line):
-        if not line.strip():
-            return
-
-        line = line.strip()
-
-        ip_address = None
-        canonical_hostname = None
-        aliases = []
-        comment = None
-
-        comment_parts = line.split("#", 1)
-        if len(comment_parts) > 1:
-            comment = comment_parts[1]
-
-        data = comment_parts[0].strip()
-
-        if data:
-            fields = data.split()
-            if len(fields) < 2:
-                raise ValueError("Invalid hosts line")
-
-            ip_address = fields[0]
-            canonical_hostname = fields[1]
-            aliases = fields[2:]
-
-        return cls(ip_address, canonical_hostname, aliases, comment)
-
-
-class HostsFile(object):
-    def __init__(self):
-        self.data = []
-        self.by_hostname = {}
-
-    def set_host(self, host):
-        if host.canonical_hostname is None:
-            self.data.append(host)
-        elif host.canonical_hostname in self.by_hostname:
-            old_host = self.by_hostname[host.canonical_hostname]
-            old_host.ip_address = host.ip_address
-            old_host.aliases = host.aliases
-            old_host.comment = host.comment
-        else:
-            self.data.append(host)
-            self.by_hostname[host.canonical_hostname] = host
-
-    @classmethod
-    def from_file(cls, f):
-        rv = cls()
-        for line in f:
-            host = HostsLine.from_string(line)
-            if host is not None:
-                rv.set_host(host)
-        return rv
-
-    def to_string(self):
-        field_widths = [0, 0]
-        for line in self.data:
-            if line.ip_address is not None:
-                field_widths[0] = max(field_widths[0], len(line.ip_address))
-                field_widths[1] = max(field_widths[1], len(line.canonical_hostname))
-
-        lines = []
-
-        for host in self.data:
-            line = ""
-            if host.ip_address is not None:
-                ip_string = host.ip_address.ljust(field_widths[0])
-                hostname_str = host.canonical_hostname
-                if host.aliases:
-                    hostname_str = "%s %s" % (hostname_str.ljust(field_widths[1]),
-                                              " ".join(host.aliases))
-                line = "%s %s" % (ip_string, hostname_str)
-            if host.comment:
-                if line:
-                    line += " "
-                line += "#%s" % host.comment
-            lines.append(line)
-
-        lines.append("")
-
-        return "\n".join(lines)
-
-    def to_file(self, f):
-        f.write(self.to_string().encode("utf8"))
new file mode 100644
--- a/testing/web-platform/tests/tools/wptrunner/wptrunner/tests/browsers/test_sauce.py
+++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/tests/browsers/test_sauce.py
@@ -15,16 +15,19 @@ def test_sauceconnect_success():
             mock.patch.object(sauce.os.path, "exists") as exists:
         # Act as if it's still running
         Popen.return_value.poll.return_value = None
         Popen.return_value.returncode = None
         # Act as if file created
         exists.return_value = True
 
         sauce_connect = sauce.SauceConnect(
+            config={
+                "domains": {"": "example.net"}
+            },
             sauce_user="aaa",
             sauce_key="bbb",
             sauce_tunnel_id="ccc",
             sauce_connect_binary="ddd")
 
         sauce_connect.__enter__(None)
 
 
@@ -41,16 +44,19 @@ def test_sauceconnect_failure_exit(ready
             mock.patch.object(sauce.subprocess, "Popen") as Popen,\
             mock.patch.object(sauce.os.path, "exists") as exists,\
             mock.patch.object(sauce.time, "sleep") as sleep:
         Popen.return_value.poll.return_value = returncode
         Popen.return_value.returncode = returncode
         exists.return_value = readyfile
 
         sauce_connect = sauce.SauceConnect(
+            config={
+                "domains": {"": "example.net"}
+            },
             sauce_user="aaa",
             sauce_key="bbb",
             sauce_tunnel_id="ccc",
             sauce_connect_binary="ddd")
 
         with pytest.raises(sauce.SauceException):
             sauce_connect.__enter__(None)
 
@@ -63,22 +69,56 @@ def test_sauceconnect_failure_never_read
             mock.patch.object(sauce.subprocess, "Popen") as Popen,\
             mock.patch.object(sauce.os.path, "exists") as exists,\
             mock.patch.object(sauce.time, "sleep") as sleep:
         Popen.return_value.poll.return_value = None
         Popen.return_value.returncode = None
         exists.return_value = False
 
         sauce_connect = sauce.SauceConnect(
+            config={
+                "domains": {"": "example.net"}
+            },
             sauce_user="aaa",
             sauce_key="bbb",
             sauce_tunnel_id="ccc",
             sauce_connect_binary="ddd")
 
         with pytest.raises(sauce.SauceException):
             sauce_connect.__enter__(None)
 
         # We should sleep while waiting for it to create the readyfile
         sleep.assert_called()
 
         # Check we actually kill it after termination fails
         Popen.return_value.terminate.assert_called()
         Popen.return_value.kill.assert_called()
+
+
+def test_sauceconnect_tunnel_domains():
+    with mock.patch.object(sauce.SauceConnect, "upload_prerun_exec"),\
+            mock.patch.object(sauce.subprocess, "Popen") as Popen,\
+            mock.patch.object(sauce.os.path, "exists") as exists:
+        Popen.return_value.poll.return_value = None
+        Popen.return_value.returncode = None
+        exists.return_value = True
+
+        sauce_connect = sauce.SauceConnect(
+            config={
+                "domains": {"foo": "foo.bar.example.com", "": "example.net"}
+            },
+            sauce_user="aaa",
+            sauce_key="bbb",
+            sauce_tunnel_id="ccc",
+            sauce_connect_binary="ddd")
+
+        sauce_connect.__enter__(None)
+
+        Popen.assert_called_once()
+        args, kwargs = Popen.call_args
+        cmd = args[0]
+        assert "--tunnel-domains" in cmd
+        i = cmd.index("--tunnel-domains")
+        rest = cmd[i+1:]
+        assert len(rest) >= 1
+        if len(rest) > 1:
+            assert rest[1].startswith("-"), "--tunnel-domains takes a comma separated list (not a space separated list)"
+        assert set(rest[0].split(",")) == {"foo.bar.example.com", "example.net"}
deleted file mode 100644
--- a/testing/web-platform/tests/tools/wptrunner/wptrunner/tests/test_hosts.py
+++ /dev/null
@@ -1,54 +0,0 @@
-import unittest
-import sys
-from os.path import join, dirname
-from cStringIO import StringIO
-
-sys.path.insert(0, join(dirname(__file__), "..", ".."))
-
-from wptrunner import hosts
-
-
-class HostsTest(unittest.TestCase):
-    def do_test(self, input, expected):
-        host_file = hosts.HostsFile.from_file(StringIO(input))
-        self.assertEquals(host_file.to_string(), expected)
-
-    def test_simple(self):
-        self.do_test("""127.0.0.1    \tlocalhost  alias # comment
-# Another comment""",
-                     """127.0.0.1 localhost alias # comment
-# Another comment
-""")
-
-    def test_blank_lines(self):
-        self.do_test("""127.0.0.1    \tlocalhost  alias # comment
-
-\r
-    \t
-# Another comment""",
-                     """127.0.0.1 localhost alias # comment
-# Another comment
-""")
-
-    def test_whitespace(self):
-        self.do_test("""    \t127.0.0.1    \tlocalhost  alias # comment     \r
-    \t# Another comment""",
-                     """127.0.0.1 localhost alias # comment
-# Another comment
-""")
-
-    def test_alignment(self):
-        self.do_test("""127.0.0.1    \tlocalhost  alias
-192.168.1.1 another_host    another_alias
-""","""127.0.0.1   localhost    alias
-192.168.1.1 another_host another_alias
-""")
-
-    def test_multiple_same_name(self):
-        # The semantics are that we overwrite earlier entries with the same name
-        self.do_test("""127.0.0.1    \tlocalhost  alias
-192.168.1.1 localhost    another_alias""","""192.168.1.1 localhost another_alias
-""")
-
-if __name__ == "__main__":
-    unittest.main()
--- a/testing/web-platform/tests/tools/wptrunner/wptrunner/wptrunner.py
+++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/wptrunner.py
@@ -213,16 +213,17 @@ def run_tests(config, test_paths, produc
                     if test_type == "wdspec":
                         browser_cls = NullBrowser
                     else:
                         browser_cls = target_browser_cls
 
                     browser_kwargs = get_browser_kwargs(test_type,
                                                         run_info,
                                                         ssl_env=ssl_env,
+                                                        config=test_environment.config,
                                                         **kwargs)
 
                     executor_cls = executor_classes.get(test_type)
                     executor_kwargs = get_executor_kwargs(test_type,
                                                           test_environment.config,
                                                           test_environment.cache_manager,
                                                           run_info,
                                                           **kwargs)
--- a/testing/web-platform/tests/tools/wptserve/wptserve/pipes.py
+++ b/testing/web-platform/tests/tools/wptserve/wptserve/pipes.py
@@ -387,16 +387,22 @@ def template(request, content, escape_ty
         field = tokens[0][1]
 
         if field in variables:
             value = variables[field]
         elif field == "headers":
             value = request.headers
         elif field == "GET":
             value = FirstWrapper(request.GET)
+        elif field == "domains":
+            if ('not_domains' in request.server.config and
+                    tokens[1][1] in request.server.config['not_domains']):
+                value = request.server.config['not_domains']
+            else:
+                value = request.server.config['domains']
         elif field in request.server.config:
             value = request.server.config[tokens[0][1]]
         elif field == "location":
             value = {"server": "%s://%s:%s" % (request.url_parts.scheme,
                                                request.url_parts.hostname,
                                                request.url_parts.port),
                      "scheme": request.url_parts.scheme,
                      "host": "%s:%s" % (request.url_parts.hostname,