diff --git a/tools/manifest/item.py b/tools/manifest/item.py index 99df09d1320dc9..7bd857dbce98e0 100644 --- a/tools/manifest/item.py +++ b/tools/manifest/item.py @@ -293,10 +293,16 @@ class PrintRefTest(RefTest): def page_ranges(self) -> PageRanges: return cast(PageRanges, self._extras.get("page_ranges", {})) + @property + def safe_printable_inset(self) -> Optional[float]: + return self._extras.get("safe_printable_inset") + def to_json(self): # type: ignore rv = super().to_json() if self.page_ranges: rv[-1]["page_ranges"] = self.page_ranges + if self.safe_printable_inset: + rv[-1]["safe_printable_inset"] = self.safe_printable_inset return rv diff --git a/tools/manifest/sourcefile.py b/tools/manifest/sourcefile.py index 9e8d8302f6a568..efca597a4fc2f6 100644 --- a/tools/manifest/sourcefile.py +++ b/tools/manifest/sourcefile.py @@ -675,6 +675,18 @@ def page_ranges(self) -> Dict[Text, List[List[Optional[int]]]]: rv[key].append(range_parts) return rv + @cached_property + def safe_printable_inset(self) -> Optional[float]: + """The safe printable inset to simulate (in centimeters)""" + assert self.root is not None + for entry in self.root.findall(".//{http://www.w3.org/1999/xhtml}meta[@name='safe-printable-inset']"): + key_data, value = self.parse_ref_keyed_meta(entry) + result = float(value) + if result < 0: + raise ValueError("Negative value") + return result + return None + @cached_property def testharness_nodes(self) -> List[ElementTree.Element]: """List of ElementTree Elements corresponding to nodes representing a @@ -1023,6 +1035,7 @@ def manifest_items(self) -> Tuple[Text, List[ManifestItem]]: viewport_size=self.viewport_size, fuzzy=self.fuzzy, page_ranges=self.page_ranges, + safe_printable_inset=self.safe_printable_inset, testdriver=self.has_testdriver, )] diff --git a/tools/manifest/tests/test_sourcefile.py b/tools/manifest/tests/test_sourcefile.py index e77f09e2a79f99..bc120bae9f9bde 100644 --- a/tools/manifest/tests/test_sourcefile.py +++ b/tools/manifest/tests/test_sourcefile.py @@ -973,6 +973,32 @@ def test_page_ranges_invalid(page_ranges): s.page_ranges +@pytest.mark.parametrize("safe_printable_inset, expected", [ + (b"0", 0), + (b"0.5", 0.5), + (b"2.54", 2.54), + (b"100", 100)]) +def test_safe_printable_inset(safe_printable_inset, expected): + content = b""" + +""" % safe_printable_inset + + s = create("foo/test-print.html", content) + + assert s.safe_printable_inset == expected + + +@pytest.mark.parametrize("safe_printable_inset", [b"ananas", b"auto", b"-1", b"0,0"]) +def test_safe_printable_inset_invalid(safe_printable_inset): + content = b""" + +""" % safe_printable_inset + + s = create("foo/test-print.html", content) + with pytest.raises(ValueError): + s.safe_printable_inset + + def test_hash(): s = SourceFile("/", "foo", "/", contents=b"Hello, World!") assert "b45ef6fec89518d314f546fd6c3025367b721684" == s.hash diff --git a/tools/webdriver/webdriver/client.py b/tools/webdriver/webdriver/client.py index 8fcb663398dde1..28c10d98ffb36e 100644 --- a/tools/webdriver/webdriver/client.py +++ b/tools/webdriver/webdriver/client.py @@ -791,6 +791,7 @@ def print( orientation=None, page=None, page_ranges=None, + safe_printable_inset=None, scale=None, shrink_to_fit=None, ): @@ -801,6 +802,7 @@ def print( "orientation": orientation, "page": page, "pageRanges": page_ranges, + "safePrintableInset": safe_printable_inset, "scale": scale, "shrinkToFit": shrink_to_fit, }.items(): diff --git a/tools/wptrunner/wptrunner/executors/base.py b/tools/wptrunner/wptrunner/executors/base.py index dd10aca589c72f..595fe6c39e68fc 100644 --- a/tools/wptrunner/wptrunner/executors/base.py +++ b/tools/wptrunner/wptrunner/executors/base.py @@ -418,11 +418,11 @@ def teardown(self): def logger(self): return self.executor.logger - def get_hash(self, test, viewport_size, dpi, page_ranges): - key = (test.url, viewport_size, dpi) + def get_hash(self, test, viewport_size, dpi, page_ranges, safe_printable_inset): + key = (test.url, viewport_size, dpi, safe_printable_inset) if key not in self.screenshot_cache: - success, data = self.get_screenshot_list(test, viewport_size, dpi, page_ranges) + success, data = self.get_screenshot_list(test, viewport_size, dpi, page_ranges, safe_printable_inset) if not success: return False, data @@ -524,9 +524,9 @@ def run_test(self, test): viewport_size = test.viewport_size dpi = test.dpi page_ranges = test.page_ranges + safe_printable_inset = test.safe_printable_inset self.message = [] - # Depth-first search of reference tree, with the goal # of reachings a leaf node with only pass results @@ -541,7 +541,7 @@ def run_test(self, test): fuzzy = self.get_fuzzy(test, nodes, relation) for i, node in enumerate(nodes): - success, data = self.get_hash(node, viewport_size, dpi, page_ranges) + success, data = self.get_hash(node, viewport_size, dpi, page_ranges, safe_printable_inset) if success is False: return {"status": data[0], "message": data[1]} @@ -575,7 +575,7 @@ def run_test(self, test): for i, (node, screenshot) in enumerate(zip(nodes, screenshots)): if screenshot is None: - success, screenshot = self.retake_screenshot(node, viewport_size, dpi, page_ranges) + success, screenshot = self.retake_screenshot(node, viewport_size, dpi, page_ranges, safe_printable_inset) if success: screenshots[i] = screenshot @@ -606,11 +606,12 @@ def get_fuzzy(self, root_test, test_nodes, relation): break return value - def retake_screenshot(self, node, viewport_size, dpi, page_ranges): + def retake_screenshot(self, node, viewport_size, dpi, page_ranges, safe_printable_inset): success, data = self.get_screenshot_list(node, viewport_size, dpi, - page_ranges) + page_ranges, + safe_printable_inset) if not success: return False, data @@ -641,8 +642,8 @@ def get_png_dimensions( return struct.unpack(">LL", image_data[16:24]) - def get_screenshot_list(self, node, viewport_size, dpi, page_ranges): - success, data = self.executor.screenshot(node, viewport_size, dpi, page_ranges) + def get_screenshot_list(self, node, viewport_size, dpi, page_ranges, safe_printable_inset): + success, data = self.executor.screenshot(node, viewport_size, dpi, page_ranges, safe_printable_inset) viewport_size = (800, 600) if viewport_size is None else viewport_size dpi = 96 if dpi is None else dpi dpcm = dpi / 2.54 diff --git a/tools/wptrunner/wptrunner/executors/executoredge.py b/tools/wptrunner/wptrunner/executors/executoredge.py index 2ddfdb3ad8f47a..3b1e3a28c6ba24 100644 --- a/tools/wptrunner/wptrunner/executors/executoredge.py +++ b/tools/wptrunner/wptrunner/executors/executoredge.py @@ -35,7 +35,7 @@ def setup(self, runner, protocol=None): with open(os.path.join(here, "reftest.js")) as f: self.script = f.read() - def screenshot(self, test, viewport_size, dpi, page_ranges): + def screenshot(self, test, viewport_size, dpi, page_ranges, safe_printable_inset): # https://github.com/web-platform-tests/wpt/issues/7140 assert dpi is None @@ -62,7 +62,7 @@ def _render(self, protocol, url, timeout): protocol.base.execute_script(self.wait_script, asynchronous=True) - pdf = protocol.pdf_print.render_as_pdf(*self.viewport_size) + pdf = protocol.pdf_print.render_as_pdf(*self.viewport_size, self.safe_printable_inset) screenshots = protocol.pdf_print.pdf_to_png(pdf, self.page_ranges) for i, screenshot in enumerate(screenshots): # strip off the data:img/png, part of the url diff --git a/tools/wptrunner/wptrunner/executors/executormarionette.py b/tools/wptrunner/wptrunner/executors/executormarionette.py index ce9462f0523317..fb916bcf4b0095 100644 --- a/tools/wptrunner/wptrunner/executors/executormarionette.py +++ b/tools/wptrunner/wptrunner/executors/executormarionette.py @@ -683,7 +683,7 @@ def load_runner(self): raise self.runner_handle = self.marionette.current_window_handle - def render_as_pdf(self, width, height): + def render_as_pdf(self, width, height, safe_printable_inset): margin = 0.5 * 2.54 body = { "page": { @@ -701,7 +701,7 @@ def render_as_pdf(self, width, height): } return self.marionette._send_message("WebDriver:Print", body, key="value") - def pdf_to_png(self, pdf_base64, page_ranges): + def pdf_to_png(self, pdf_base64, page_ranges, safe_printable_inset): handle = self.marionette.current_window_handle _switch_to_window(self.marionette, self.runner_handle) try: @@ -1227,7 +1227,7 @@ def do_test(self, test): return self.convert_result(test, result) - def screenshot(self, test, viewport_size, dpi, page_ranges): + def screenshot(self, test, viewport_size, dpi, page_ranges, safe_printable_inset): # https://github.com/web-platform-tests/wpt/issues/7135 assert viewport_size is None assert dpi is None @@ -1431,7 +1431,7 @@ def setup(self, runner, protocol=None): if not isinstance(self.implementation, InternalRefTestImplementation): self.protocol.pdf_print.load_runner() - def screenshot(self, test, viewport_size, dpi, page_ranges): + def screenshot(self, test, viewport_size, dpi, page_ranges, safe_printable_inset): # https://github.com/web-platform-tests/wpt/issues/7140 assert dpi is None @@ -1453,7 +1453,7 @@ def _render(self, protocol, url, timeout): protocol.base.execute_script(self.wait_script, asynchronous=True) - pdf = protocol.pdf_print.render_as_pdf(*self.viewport_size) + pdf = protocol.pdf_print.render_as_pdf(*self.viewport_size, self.safe_printable_inset) screenshots = protocol.pdf_print.pdf_to_png(pdf, self.page_ranges) for i, screenshot in enumerate(screenshots): # strip off the data:img/png, part of the url diff --git a/tools/wptrunner/wptrunner/executors/executorselenium.py b/tools/wptrunner/wptrunner/executors/executorselenium.py index 0080367f6ed194..73f041e15bc069 100644 --- a/tools/wptrunner/wptrunner/executors/executorselenium.py +++ b/tools/wptrunner/wptrunner/executors/executorselenium.py @@ -442,7 +442,7 @@ def do_test(self, test): return self.convert_result(test, result) - def screenshot(self, test, viewport_size, dpi, page_ranges): + def screenshot(self, test, viewport_size, dpi, page_ranges, safe_printable_inset): # https://github.com/web-platform-tests/wpt/issues/7135 assert viewport_size is None assert dpi is None diff --git a/tools/wptrunner/wptrunner/executors/executorservolegacy.py b/tools/wptrunner/wptrunner/executors/executorservolegacy.py index a03c2d4ee6fff5..8118845b96d7da 100644 --- a/tools/wptrunner/wptrunner/executors/executorservolegacy.py +++ b/tools/wptrunner/wptrunner/executors/executorservolegacy.py @@ -244,7 +244,7 @@ def teardown(self): os.rmdir(self.tempdir) super().teardown() - def screenshot(self, test, viewport_size, dpi, page_ranges): + def screenshot(self, test, viewport_size, dpi, page_ranges, safe_printable_inset): with TempFilename(self.tempdir) as output_path: extra_args = ["--exit", "--output=%s" % output_path, diff --git a/tools/wptrunner/wptrunner/executors/executorwebdriver.py b/tools/wptrunner/wptrunner/executors/executorwebdriver.py index 808d96f61d00e7..47d7343a44dd17 100644 --- a/tools/wptrunner/wptrunner/executors/executorwebdriver.py +++ b/tools/wptrunner/wptrunner/executors/executorwebdriver.py @@ -519,7 +519,7 @@ def load_runner(self): raise self.runner_handle = self.webdriver.window_handle - def render_as_pdf(self, width, height): + def render_as_pdf(self, width, height, safe_printable_inset_param): # All units passed to `print()` are in cm. See [0] for testing specifications. # # [0]: https://web-platform-tests.org/writing-tests/print-reftests.html @@ -527,6 +527,7 @@ def render_as_pdf(self, width, height): pdf_base64 = self.webdriver.print(page={"width": width, "height": height}, margin={"top": margin, "right": margin, "bottom": margin, "left": margin}, + safe_printable_inset=safe_printable_inset_param, background=True, shrink_to_fit=False) return pdf_base64 @@ -1383,7 +1384,7 @@ def do_test(self, test): return self.convert_result(test, result) - def screenshot(self, test, viewport_size, dpi, page_ranges): + def screenshot(self, test, viewport_size, dpi, page_ranges, safe_printable_inset): # https://github.com/web-platform-tests/wpt/issues/7135 assert viewport_size is None assert dpi is None @@ -1423,7 +1424,7 @@ def setup(self, runner, protocol=None): with open(os.path.join(here, "reftest.js")) as f: self.script = f.read() - def screenshot(self, test, viewport_size, dpi, page_ranges): + def screenshot(self, test, viewport_size, dpi, page_ranges, safe_printable_inset): # https://github.com/web-platform-tests/wpt/issues/7140 assert dpi is None @@ -1434,6 +1435,7 @@ def screenshot(self, test, viewport_size, dpi, page_ranges): self.viewport_size = viewport_size self.page_ranges = page_ranges.get(test.url) + self.safe_printable_inset = safe_printable_inset timeout = self.timeout_multiplier * test.timeout if self.debug_info is None else None test_url = self.test_url(test) @@ -1449,7 +1451,7 @@ def _render(self, protocol, url, timeout): # return value. protocol.testdriver.run(url, self.wait_script) - pdf = protocol.pdf_print.render_as_pdf(*self.viewport_size) + pdf = protocol.pdf_print.render_as_pdf(*self.viewport_size, self.safe_printable_inset) screenshots = protocol.pdf_print.pdf_to_png(pdf, self.page_ranges) for i, screenshot in enumerate(screenshots): # strip off the data:img/png, part of the url diff --git a/tools/wptrunner/wptrunner/executors/protocol.py b/tools/wptrunner/wptrunner/executors/protocol.py index b7dfac9de960dc..53744970b0983d 100644 --- a/tools/wptrunner/wptrunner/executors/protocol.py +++ b/tools/wptrunner/wptrunner/executors/protocol.py @@ -1134,7 +1134,7 @@ class PrintProtocolPart(ProtocolPart): name = "pdf_print" @abstractmethod - def render_as_pdf(self, width, height): + def render_as_pdf(self, width, height, safe_printable_inset): """Output document as PDF""" pass diff --git a/tools/wptrunner/wptrunner/wpttest.py b/tools/wptrunner/wptrunner/wpttest.py index 9616f9c429f309..0ba61e5803801d 100644 --- a/tools/wptrunner/wptrunner/wpttest.py +++ b/tools/wptrunner/wptrunner/wpttest.py @@ -699,22 +699,28 @@ def fuzzy_override(self): def page_ranges(self): return {} + @property + def safe_printable_inset(self): + return 0 class PrintReftestTest(ReftestTest): test_type = "print-reftest" def __init__(self, url_base, tests_root, url, inherit_metadata, test_metadata, references, timeout=None, path=None, viewport_size=None, dpi=None, fuzzy=None, - page_ranges=None, protocol="http", subdomain=False, testdriver=False): + page_ranges=None, safe_printable_inset=None, protocol="http", subdomain=False, + testdriver=False): super().__init__(url_base, tests_root, url, inherit_metadata, test_metadata, references, timeout, path, viewport_size, dpi, fuzzy, protocol, subdomain=subdomain, testdriver=testdriver) self._page_ranges = page_ranges + self._safe_printable_inset = safe_printable_inset @classmethod def cls_kwargs(cls, manifest_test): rv = super().cls_kwargs(manifest_test) rv["page_ranges"] = manifest_test.page_ranges + rv["safe_printable_inset"] = manifest_test.safe_printable_inset return rv def get_viewport_size(self, override): @@ -725,6 +731,9 @@ def get_viewport_size(self, override): def page_ranges(self): return self._page_ranges + @property + def safe_printable_inset(self): + return self._safe_printable_inset class WdspecTest(Test): result_cls = WdspecResult