Prototype of the "Set Timezone WebDrive extension"
This CL is NOT intended to be merge into chromium.
I just want to use the chromium tryBot to validate the code is correct.
The WPT PR is in https://github.com/web-platform-tests/wpt/pull/26555
Bug: 1144403
Change-Id: Ie3c062d1d9e59ee3f9087c7af99df7d2d29b056d
diff --git a/docs/writing-tests/testdriver.md b/docs/writing-tests/testdriver.md
index 931499d..5214cfb 100644
--- a/docs/writing-tests/testdriver.md
+++ b/docs/writing-tests/testdriver.md
@@ -182,6 +182,23 @@
await test_driver.set_permission({ name: "push", userVisibleOnly: true }, "granted", true);
```
+### set_time_zone
+
+Usage: `test_driver.set_time_zone(time_zone)`
+ * _timezone_: a string matching one of the
+ [Zone or Link names of the IANA Time Zone Database](https://www.iana.org/time-zones)
+
+This function set the host default time zone to the given time zone ID.
+It returns a promise that resolves after the time zone has
+been set to be overridden with _time_zone_.
+
+Example:
+
+``` js
+await test_driver.set_time_zone("Asia/Taipei");
+await test_driver.set_time_zone("Asia/Hong_Kong");
+```
+
## Using testdriver in Other Browsing Contexts
Testdriver can be used in browsing contexts (i.e. windows or frames)
diff --git a/infrastructure/testdriver/set_timezone.html b/infrastructure/testdriver/set_timezone.html
new file mode 100644
index 0000000..0d514f5
--- /dev/null
+++ b/infrastructure/testdriver/set_timezone.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>TestDriver set_time_zone method</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<script>
+function defaultTimeZone() {
+ return (new Intl.DateTimeFormat()).resolvedOptions().timeZone;
+}
+async_test(t => {
+ let timeZone = "Asia/Taipei";
+ test_driver
+ .set_time_zone(timeZone)
+ .then(() => {
+ assert_equals(defaultTimeZone(), timeZone);
+ t.done()
+ })
+ .catch(() => assert_unreached("set_time_zone failed"));
+});
+</script>
diff --git a/resources/testdriver.js b/resources/testdriver.js
index f3cee98..d1aae9e 100644
--- a/resources/testdriver.js
+++ b/resources/testdriver.js
@@ -455,6 +455,26 @@
const blocked = state === "blocked";
return window.test_driver_internal.set_storage_access(origin, embedding_origin, blocked, context);
},
+
+ /**
+ * Set time zone.
+ *
+ * The set_time_zone function set the host default time zone to a new
+ * timezone id.
+ *
+ * This matches the behavior of the {@link
+ * https://github.com/whatwg/html/pull/3047
+ * Set Time Zone command}.
+ *
+ * @param {String} time_zone - IANA Time Zone ID.
+ *
+ * @returns {Promise} fulfilled after the time zone is set, or
+ * rejected if the set of time zone fails
+ */
+ set_time_zone: function(time_zone) {
+ return window.test_driver_internal.set_time_zone(time_zone);
+ },
+
};
window.test_driver_internal = {
@@ -560,5 +580,9 @@
set_storage_access: function(origin, embedding_origin, blocked, context=null) {
return Promise.reject(new Error("unimplemented"));
},
+
+ set_time_zone: function(time_zone) {
+ return Promise.reject(new Error("unimplemented"));
+ },
};
})();
diff --git a/service-workers/timezonechange/resources/service-worker-timezonechange.js b/service-workers/timezonechange/resources/service-worker-timezonechange.js
new file mode 100644
index 0000000..12a9ae0
--- /dev/null
+++ b/service-workers/timezonechange/resources/service-worker-timezonechange.js
@@ -0,0 +1,14 @@
+self.addEventListener('message', function(e) {
+ const message = e.data;
+ if ('port' in message) {
+ const port = message.port;
+ const oldTimeZone =
+ (new Intl.DateTimeFormat()).resolvedOptions().timeZone;
+ self.addEventListener('timezonechange', function(evt) {
+ const newTimeZone =
+ (new Intl.DateTimeFormat()).resolvedOptions().timeZone;
+ port.postMessage('SUCCESS:' + newTimeZone);
+ });
+ port.postMessage('READY:' + oldTimeZone);
+ }
+});
diff --git a/service-workers/timezonechange/service-worker-timezonechange.https.html b/service-workers/timezonechange/service-worker-timezonechange.https.html
new file mode 100644
index 0000000..b7a278c
--- /dev/null
+++ b/service-workers/timezonechange/service-worker-timezonechange.https.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<title>Service Worker: timezonechange event</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="../service-worker/resources/test-helpers.sub.js"></script>
+<script>
+
+function defaultTimeZone() {
+ return (new Intl.DateTimeFormat()).resolvedOptions().timeZone;
+}
+
+promise_test(async t => {
+ const oldTimeZone = "Pacific/Fakaofo";
+ const newTimeZone = "Asia/Taipei";
+ // First, we set to a fixed time zone.
+ await window.test_driver.set_time_zone(oldTimeZone);
+ assert_equals(defaultTimeZone(), oldTimeZone);
+
+ const script = 'resources/service-worker-timezonechange.js';
+ const scope = 'resources/blank.html';
+ let worker;
+ let port;
+
+ return service_worker_unregister_and_register(t, script, scope)
+ .then(registration => {
+ t.add_cleanup(() => registration.unregister());
+ worker = registration.installing;
+
+ const messageChannel = new MessageChannel();
+ port = messageChannel.port1;
+ return new Promise(resolve => {
+ port.onmessage = resolve;
+ worker.postMessage({port: messageChannel.port2},
+ [messageChannel.port2]);
+ });
+ })
+ .then(e => {
+ assert_equals(e.data, 'READY:' + oldTimeZone);
+ return new Promise(async resolve => {
+ port.onmessage = resolve;
+ // Change the time zone once the service worker is ready.
+ await window.test_driver.set_time_zone(newTimeZone);
+ });
+ })
+ .then(e => {
+ assert_equals(e.data, 'SUCCESS:' + newTimeZone);
+ });
+}, 'timezonechange event work in ServiceWorker');
+</script>
diff --git a/timezonechange/addeventlistener-timezonechange-fired.html b/timezonechange/addeventlistener-timezonechange-fired.html
new file mode 100644
index 0000000..2477077
--- /dev/null
+++ b/timezonechange/addeventlistener-timezonechange-fired.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+
+function defaultTimeZone() {
+ return (new Intl.DateTimeFormat()).resolvedOptions().timeZone;
+}
+promise_test(async t => {
+ const oldTimeZone = "Pacific/Fakaofo";
+ const newTimeZone = "Asia/Taipei";
+ // First, setup the time zone in a fixed place.
+ await window.test_driver.set_time_zone(oldTimeZone);
+ assert_equals(defaultTimeZone(), oldTimeZone);
+ return new Promise(r => {
+ window.addEventListener('timezonechange', r);
+ window.test_driver.set_time_zone(newTimeZone);
+ }).then(e => {
+ assert_equals(defaultTimeZone(), newTimeZone);
+ });
+}, "Test that the timezonechange event fires on window.addEventListener('timezonechange')");
+</script>
+</body>
+</html>
diff --git a/timezonechange/ontimezonechange-event-property.html b/timezonechange/ontimezonechange-event-property.html
new file mode 100644
index 0000000..4025209
--- /dev/null
+++ b/timezonechange/ontimezonechange-event-property.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+function defaultTimeZone() {
+ return (new Intl.DateTimeFormat()).resolvedOptions().timeZone;
+}
+promise_test(async t => {
+ const oldTimeZone = "Pacific/Fakaofo";
+ const newTimeZone = "Asia/Taipei";
+ // First, setup the time zone in a fixed place.
+ await window.test_driver.set_time_zone(oldTimeZone);
+ assert_equals(defaultTimeZone(), oldTimeZone);
+ return new Promise(async r => {
+ window.addEventListener('timezonechange', r);
+ await window.test_driver.set_time_zone(newTimeZone);
+ }).then(e => {
+ assert_false(e.cancelable);
+ assert_false(e.bubbles);
+ assert_equals(defaultTimeZone(), newTimeZone);
+ });
+}, "Test properties of the fired event.");
+
+</script>
+</body>
+</html>
diff --git a/timezonechange/ontimezonechange-over-setattribute.html b/timezonechange/ontimezonechange-over-setattribute.html
new file mode 100644
index 0000000..8a23e8b
--- /dev/null
+++ b/timezonechange/ontimezonechange-over-setattribute.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+function defaultTimeZone() {
+ return (new Intl.DateTimeFormat()).resolvedOptions().timeZone;
+}
+promise_test(async t => {
+ const oldTimeZone = "Pacific/Fakaofo";
+ const newTimeZone = "Asia/Taipei";
+ // First, setup the time_zone in a fixed place.
+ await window.test_driver.set_time_zone(oldTimeZone);
+ assert_equals(defaultTimeZone(), oldTimeZone);
+ window.received = 0; // We need a global variable here.
+ var fromWindowHandler = false;
+ document.body.setAttribute('ontimezonechange', 'window.received++;');
+
+ return new Promise(async r => {
+ window.ontimezonechange = r;
+ await window.test_driver.set_time_zone(newTimeZone);
+ }).then(evt => {
+ received++;
+ assert_equals(window.received, 1);
+ assert_equals(defaultTimeZone(), newTimeZone);
+ });
+}, "Test that the timezonechange event fires on window.ontimezonechange but not body ontimezonechange attribute");
+</script>
+</body>
+</html>
diff --git a/timezonechange/ontimezonechange.html b/timezonechange/ontimezonechange.html
new file mode 100644
index 0000000..a0c7ca3
--- /dev/null
+++ b/timezonechange/ontimezonechange.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+
+test(function() {
+ assert_true('ontimezonechange' in window);
+}, "Test that timezonechange event handler API is present in window");
+
+</script>
+</body>
+</html>
diff --git a/timezonechange/resources/shared-worker-timezonechange.js b/timezonechange/resources/shared-worker-timezonechange.js
new file mode 100644
index 0000000..48c280f
--- /dev/null
+++ b/timezonechange/resources/shared-worker-timezonechange.js
@@ -0,0 +1,9 @@
+onconnect = connectEvent => {
+ let oldtimezone = (new Intl.DateTimeFormat()).resolvedOptions().timeZone;
+ const port = connectEvent.ports[0];
+ ontimezonechange = () => {
+ let timezone = (new Intl.DateTimeFormat()).resolvedOptions().timeZone;
+ port.postMessage("SUCCESS:" + timezone);
+ };
+ port.postMessage("READY:" + oldtimezone); // (the html will change the timezone)
+}
diff --git a/timezonechange/resources/worker-timezonechange.js b/timezonechange/resources/worker-timezonechange.js
new file mode 100644
index 0000000..a0aeb0b
--- /dev/null
+++ b/timezonechange/resources/worker-timezonechange.js
@@ -0,0 +1,6 @@
+let oldtimezone = (new Intl.DateTimeFormat()).resolvedOptions().timeZone;
+ontimezonechange = evt => {
+ let timezone = (new Intl.DateTimeFormat()).resolvedOptions().timeZone;
+ postMessage("SUCCESS:" + timezone);
+}
+postMessage("READY:" + oldtimezone);
diff --git a/timezonechange/setattribute-over-ontimezonechange.html b/timezonechange/setattribute-over-ontimezonechange.html
new file mode 100644
index 0000000..22de3e2
--- /dev/null
+++ b/timezonechange/setattribute-over-ontimezonechange.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+function defaultTimeZone() {
+ return (new Intl.DateTimeFormat()).resolvedOptions().timeZone;
+}
+promise_test(async t => {
+ const oldTimeZone = "Pacific/Fakaofo";
+ const newTimeZone = "Asia/Taipei";
+ // First, setup the time zone in a fixed place.
+ await window.test_driver.set_time_zone(oldTimeZone);
+ assert_equals(defaultTimeZone(), oldTimeZone);
+ window.received = 0; // We need a global variable here.
+ window.ontimezonechange = () => received++;
+ document.body.setAttribute('ontimezonechange', () => {
+ received++;
+ assert_equals(window.received, 1);
+ assert_equals(defaultTimeZone(), newTimeZone);
+ });
+ await window.test_driver.set_time_zone(newTimeZone);
+}, "Test that the timezonechange event fires on body ontimezonechange attribute but not window.ontimezonechange");
+</script>
+</body>
+</html>
diff --git a/timezonechange/setattribute.html b/timezonechange/setattribute.html
new file mode 100644
index 0000000..0af1284
--- /dev/null
+++ b/timezonechange/setattribute.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+function defaultTimeZone() {
+ return (new Intl.DateTimeFormat()).resolvedOptions().timeZone;
+}
+promise_test(async t => {
+ const oldTimeZone = "Pacific/Fakaofo";
+ const newTimeZone = "Asia/Taipei";
+ // First, setup the time zone in a fixed place.
+ await window.test_driver.set_time_zone(oldTimeZone);
+ assert_equals(defaultTimeZone(), oldTimeZone);
+ window.received = false; // We need a global variable here.
+ document.body.setAttribute('ontimezonechange', 'window.received = true;');
+
+ await window.test_driver.set_time_zone(newTimeZone);
+ assert_true(window.received);
+ assert_equals(defaultTimeZone(), newTimeZone);
+}, "Test that the timezonechange event fires on body ontimezonechange attribute");
+
+</script>
+</body>
+</html>
diff --git a/timezonechange/shared-worker-timezonechange.html b/timezonechange/shared-worker-timezonechange.html
new file mode 100644
index 0000000..6040c90
--- /dev/null
+++ b/timezonechange/shared-worker-timezonechange.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Test shared worker handle ontimezonechange event.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+promise_test(async t => {
+ const oldTimeZone = "Pacific/Fakaofo";
+ const newTimeZone = "Asia/Taipei";
+ // First we set the time zone to a fixed time zone.
+ await window.test_driver.set_time_zone(oldTimeZone);
+ const worker = new SharedWorker('resources/shared-worker-timezonechange.js', 'name');
+ return new Promise(r => { worker.port.onmessage = r; })
+ .then(e => {
+ // Once we know the worker is ready, we change the time zone.
+ assert_equals(e.data, "READY:" + oldTimeZone);
+ return new Promise(async r => {
+ worker.port.onmessage = r;
+ await window.test_driver.set_time_zone(newTimeZone);
+ }).then(e => {
+ assert_equals(e.data, "SUCCESS:" + newTimeZone);
+ })
+ });
+}, "Test a shared worker handles ontimezonechange event.");
+</script>
diff --git a/timezonechange/window-ontimezonechange-fired.html b/timezonechange/window-ontimezonechange-fired.html
new file mode 100644
index 0000000..150f6d5
--- /dev/null
+++ b/timezonechange/window-ontimezonechange-fired.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+function defaultTimeZone() {
+ return (new Intl.DateTimeFormat()).resolvedOptions().timeZone;
+}
+promise_test(async t => {
+ const oldTimeZone = "Pacific/Fakaofo";
+ const newTimeZone = "Asia/Taipei";
+ // First, setup the time zone in a fixed place.
+ await window.test_driver.set_time_zone(oldTimeZone);
+ assert_equals(defaultTimeZone(), oldTimeZone);
+ return new Promise(async r => {
+ window.ontimezonechange = r;
+ await window.test_driver.set_time_zone(newTimeZone);
+ }).then(e => {
+ assert_equals(defaultTimeZone(), newTimeZone);
+ });
+}, "Test that the timezonechange event fires on window.ontimezonechange");
+
+</script>
+</body>
+</html>
diff --git a/timezonechange/worker-timezonechange.html b/timezonechange/worker-timezonechange.html
new file mode 100644
index 0000000..cb673b9
--- /dev/null
+++ b/timezonechange/worker-timezonechange.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Test worker handle ontimezonechange event.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+promise_test(async t => {
+ const oldTimeZone = "Pacific/Fakaofo";
+ const newTimeZone = "Asia/Taipei";
+ // First we set the time zone to a fixed time zone.
+ await window.test_driver.set_time_zone(oldTimeZone);
+ const worker = new Worker('resources/worker-timezonechange.js');
+ return new Promise(r => { worker.onmessage = r; })
+ .then(e => {
+ // Once we know the worker is ready, we change the timezone.
+ assert_equals(e.data, "READY:" + oldTimeZone);
+ return new Promise(async r => {
+ worker.onmessage = r;
+ await window.test_driver.set_time_zone(newTimeZone);
+ }).then(e => {
+ assert_equals(e.data, "SUCCESS:" + newTimeZone);
+ })
+ });
+}, "Test a dedicated worker handles ontimezonechange event.");
+</script>
diff --git a/tools/wptrunner/wptrunner/executors/actions.py b/tools/wptrunner/wptrunner/executors/actions.py
index 963ed33..db41d73 100644
--- a/tools/wptrunner/wptrunner/executors/actions.py
+++ b/tools/wptrunner/wptrunner/executors/actions.py
@@ -181,6 +181,18 @@
"Setting user verified flag on authenticator %s to %s" % (authenticator_id, uv["isUserVerified"]))
return self.protocol.virtual_authenticator.set_user_verified(authenticator_id, uv)
+class SetTimeZoneAction(object):
+ name = "set_time_zone"
+
+ def __init__(self, logger, protocol):
+ self.logger = logger
+ self.protocol = protocol
+
+ def __call__(self, payload):
+ time_zone = payload["time_zone"]
+ self.logger.debug("Setting time_zone to %s" % time_zone)
+ self.protocol.set_time_zone.set_time_zone(time_zone)
+
actions = [ClickAction,
DeleteAllCookiesAction,
@@ -194,4 +206,5 @@
GetCredentialsAction,
RemoveCredentialAction,
RemoveAllCredentialsAction,
- SetUserVerifiedAction]
+ SetUserVerifiedAction,
+ SetTimeZoneAction]
diff --git a/tools/wptrunner/wptrunner/executors/executorwebdriver.py b/tools/wptrunner/wptrunner/executors/executorwebdriver.py
index 43fc910..df13dbe 100644
--- a/tools/wptrunner/wptrunner/executors/executorwebdriver.py
+++ b/tools/wptrunner/wptrunner/executors/executorwebdriver.py
@@ -26,7 +26,8 @@
TestDriverProtocolPart,
GenerateTestReportProtocolPart,
SetPermissionProtocolPart,
- VirtualAuthenticatorProtocolPart)
+ VirtualAuthenticatorProtocolPart,
+ SetTimeZoneProtocolPart)
from ..testrunner import Stop
import webdriver as client
@@ -293,6 +294,14 @@
return self.webdriver.send_session_command("POST", "webauthn/authenticator/%s/uv" % authenticator_id, uv)
+class WebDriverSetTimeZoneProtocolPart(SetTimeZoneProtocolPart):
+ def setup(self):
+ self.webdriver = self.parent.webdriver
+
+ def set_time_zone(self, time_zone):
+ return self.webdriver.send_session_command("POST", "time_zone", {"time_zone": time_zone})
+
+
class WebDriverProtocol(Protocol):
implements = [WebDriverBaseProtocolPart,
WebDriverTestharnessProtocolPart,
@@ -304,7 +313,8 @@
WebDriverTestDriverProtocolPart,
WebDriverGenerateTestReportProtocolPart,
WebDriverSetPermissionProtocolPart,
- WebDriverVirtualAuthenticatorProtocolPart]
+ WebDriverVirtualAuthenticatorProtocolPart,
+ WebDriverSetTimeZoneProtocolPart]
def __init__(self, executor, browser, capabilities, **kwargs):
super(WebDriverProtocol, self).__init__(executor, browser)
diff --git a/tools/wptrunner/wptrunner/executors/protocol.py b/tools/wptrunner/wptrunner/executors/protocol.py
index 8bdc8b0..497fa57 100644
--- a/tools/wptrunner/wptrunner/executors/protocol.py
+++ b/tools/wptrunner/wptrunner/executors/protocol.py
@@ -515,3 +515,16 @@
def render_as_pdf(self, width, height):
"""Output document as PDF"""
pass
+
+class SetTimeZoneProtocolPart(ProtocolPart):
+ """Protocol part for setting time zone"""
+ __metaclass__ = ABCMeta
+
+ name = "set_time_zone"
+
+ @abstractmethod
+ def set_time_zone(self, time_zone):
+ """Set time_zone as default time zone."""
+ print("SetTimeZoneProtocolPart" + time_zone)
+ pass
+
diff --git a/tools/wptrunner/wptrunner/testdriver-extra.js b/tools/wptrunner/wptrunner/testdriver-extra.js
index e15652d..f3b1c89 100644
--- a/tools/wptrunner/wptrunner/testdriver-extra.js
+++ b/tools/wptrunner/wptrunner/testdriver-extra.js
@@ -233,4 +233,9 @@
window.test_driver_internal.set_user_verified = function(authenticator_id, uv, context=null) {
return create_action("set_user_verified", {authenticator_id, uv, context});
};
+
+ window.test_driver_internal.set_time_zone = function(time_zone) {
+ console.log("Internal set_time_zone " + time_zone);
+ return create_action("set_time_zone", {time_zone});
+ };
})();