diff --git a/README.md b/README.md index 9827c347e..a6d4a38d7 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Playwright is a Java library to automate [Chromium](https://www.chromium.org/Hom | :--- | :---: | :---: | :---: | | Chromium 148.0.7778.96 | :white_check_mark: | :white_check_mark: | :white_check_mark: | | WebKit 26.4 | ✅ | ✅ | ✅ | -| Firefox 150.0.1 | :white_check_mark: | :white_check_mark: | :white_check_mark: | +| Firefox 150.0.2 | :white_check_mark: | :white_check_mark: | :white_check_mark: | ## Documentation diff --git a/playwright/src/main/java/com/microsoft/playwright/Screencast.java b/playwright/src/main/java/com/microsoft/playwright/Screencast.java index 15307ad6b..0c0122ccd 100644 --- a/playwright/src/main/java/com/microsoft/playwright/Screencast.java +++ b/playwright/src/main/java/com/microsoft/playwright/Screencast.java @@ -27,7 +27,7 @@ public interface Screencast { class StartOptions { /** - * Callback that receives JPEG-encoded frame data. + * Callback that receives JPEG-encoded frame data along with the page viewport size at the time of capture. */ public Consumer onFrame; /** @@ -40,7 +40,7 @@ class StartOptions { public Integer quality; /** - * Callback that receives JPEG-encoded frame data. + * Callback that receives JPEG-encoded frame data along with the page viewport size at the time of capture. */ public StartOptions setOnFrame(Consumer onFrame) { this.onFrame = onFrame; diff --git a/playwright/src/main/java/com/microsoft/playwright/options/ScreencastFrame.java b/playwright/src/main/java/com/microsoft/playwright/ScreencastFrame.java similarity index 67% rename from playwright/src/main/java/com/microsoft/playwright/options/ScreencastFrame.java rename to playwright/src/main/java/com/microsoft/playwright/ScreencastFrame.java index 1b4d3e25f..ee4056ca5 100644 --- a/playwright/src/main/java/com/microsoft/playwright/options/ScreencastFrame.java +++ b/playwright/src/main/java/com/microsoft/playwright/ScreencastFrame.java @@ -14,19 +14,21 @@ * limitations under the License. */ -package com.microsoft.playwright.options; +package com.microsoft.playwright; -/** - * A single screencast frame delivered to {@link com.microsoft.playwright.Screencast#start Screencast.start()}'s - * {@code onFrame} callback. - */ -public class ScreencastFrame { +public interface ScreencastFrame { /** * JPEG-encoded frame data. */ - public byte[] data; + byte[] data(); + + /** + * Width of the page viewport at the time the frame was captured. + */ + int viewportWidth(); - public ScreencastFrame(byte[] data) { - this.data = data; - } -} + /** + * Height of the page viewport at the time the frame was captured. + */ + int viewportHeight(); +} \ No newline at end of file diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/ScreencastFrameImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/ScreencastFrameImpl.java new file mode 100644 index 000000000..d12513dbf --- /dev/null +++ b/playwright/src/main/java/com/microsoft/playwright/impl/ScreencastFrameImpl.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.microsoft.playwright.impl; + +import com.microsoft.playwright.ScreencastFrame; + +class ScreencastFrameImpl implements ScreencastFrame { + private final byte[] data; + private final int viewportWidth; + private final int viewportHeight; + + ScreencastFrameImpl(byte[] data, int viewportWidth, int viewportHeight) { + this.data = data; + this.viewportWidth = viewportWidth; + this.viewportHeight = viewportHeight; + } + + @Override + public byte[] data() { + return data; + } + + @Override + public int viewportWidth() { + return viewportWidth; + } + + @Override + public int viewportHeight() { + return viewportHeight; + } +} diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/ScreencastImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/ScreencastImpl.java index f4c8f4c8f..2e3a9cb56 100644 --- a/playwright/src/main/java/com/microsoft/playwright/impl/ScreencastImpl.java +++ b/playwright/src/main/java/com/microsoft/playwright/impl/ScreencastImpl.java @@ -19,7 +19,7 @@ import com.google.gson.JsonObject; import com.microsoft.playwright.PlaywrightException; import com.microsoft.playwright.Screencast; -import com.microsoft.playwright.options.ScreencastFrame; +import com.microsoft.playwright.ScreencastFrame; import java.nio.file.Path; import java.util.function.Consumer; @@ -44,7 +44,9 @@ void handleScreencastFrame(JsonObject params) { } String dataBase64 = params.get("data").getAsString(); byte[] data = java.util.Base64.getDecoder().decode(dataBase64); - onFrame.accept(new ScreencastFrame(data)); + int viewportWidth = params.get("viewportWidth").getAsInt(); + int viewportHeight = params.get("viewportHeight").getAsInt(); + onFrame.accept(new ScreencastFrameImpl(data, viewportWidth, viewportHeight)); } @Override diff --git a/playwright/src/test/java/com/microsoft/playwright/TestScreencast.java b/playwright/src/test/java/com/microsoft/playwright/TestScreencast.java index c95c86a48..ecf0cce7c 100644 --- a/playwright/src/test/java/com/microsoft/playwright/TestScreencast.java +++ b/playwright/src/test/java/com/microsoft/playwright/TestScreencast.java @@ -16,7 +16,6 @@ package com.microsoft.playwright; -import com.microsoft.playwright.options.ScreencastFrame; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -113,9 +112,30 @@ void screencastStartShouldDeliverFramesViaOnFrame() throws Exception { assertFalse(frames.isEmpty(), "expected at least one frame"); // JPEG-encoded frames start with FF D8. for (ScreencastFrame frame : frames) { - assertNotNull(frame.data); - assertEquals((byte) 0xFF, frame.data[0]); - assertEquals((byte) 0xD8, frame.data[1]); + assertNotNull(frame.data()); + assertEquals((byte) 0xFF, frame.data()[0]); + assertEquals((byte) 0xD8, frame.data()[1]); + } + } finally { + context.close(); + } + } + + @Test + void onFrameShouldReceiveViewportSize() { + BrowserContext context = browser.newContext(new Browser.NewContextOptions().setViewportSize(1000, 400)); + Page page = context.newPage(); + try { + List frames = new ArrayList<>(); + page.screencast().start(new Screencast.StartOptions().setOnFrame(frames::add)); + page.navigate(server.EMPTY_PAGE); + page.evaluate("() => document.body.style.backgroundColor = 'red'"); + page.waitForTimeout(500); + page.screencast().stop(); + assertFalse(frames.isEmpty(), "expected at least one frame"); + for (ScreencastFrame frame : frames) { + assertEquals(1000, frame.viewportWidth()); + assertEquals(400, frame.viewportHeight()); } } finally { context.close(); diff --git a/scripts/DRIVER_VERSION b/scripts/DRIVER_VERSION index bfba35b9c..bc2a131f6 100644 --- a/scripts/DRIVER_VERSION +++ b/scripts/DRIVER_VERSION @@ -1 +1 @@ -1.60.0-alpha-1778025033000 +1.60.0-beta-1778180503000 diff --git a/tools/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java b/tools/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java index c71566c9f..a640897d4 100644 --- a/tools/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java +++ b/tools/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java @@ -326,6 +326,22 @@ private void createClassesAndEnums(JsonObject jsonObject) { } return; } + if ("function".equals(jsonObject.get("name").getAsString()) && jsonObject.has("args")) { + for (JsonElement item : jsonObject.getAsJsonArray("args")) { + if (!item.isJsonObject()) { + continue; + } + JsonObject argObject = item.getAsJsonObject(); + if (!"Object".equals(argObject.get("name").getAsString())) { + continue; + } + String alias = javaAlias(argObject); + if (alias != null) { + typeScope().createTopLevelInterface(alias, this, argObject); + } + } + return; + } if ("Object".equals(jsonObject.get("name").getAsString())) { if (customType != null) { // Same type maybe referenced as 'Object' in several union values, e.g. Object|Array @@ -506,8 +522,8 @@ private String convertBuiltinType(JsonObject jsonType) { if (customType != null) { return customType; } - // Inner Objects (e.g. function arguments) are not visited by createClassesAndEnums, - // so resolve their Java type name from langAliases here. + // Inner Objects without langAliases (e.g. unaliased function arguments) are not visited + // by createClassesAndEnums, so resolve their Java type name from langAliases here. String alias = javaAlias(jsonType); if (alias != null) { return alias; @@ -601,6 +617,14 @@ void createTopLevelClass(String name, Element parent, JsonObject jsonObject) { } } + void createTopLevelInterface(String name, Element parent, JsonObject jsonObject) { + Map map = topLevelTypes(); + TypeDefinition existing = map.putIfAbsent(name, new CustomInterface(parent, name, jsonObject)); + if (existing != null && !(existing instanceof CustomInterface)) { + throw new RuntimeException("Two interfaces with same name have different values:\n" + jsonObject + "\n" + existing.jsonElement); + } + } + void createNestedClass(String name, Element parent, JsonObject jsonObject) { for (CustomClass c : classes) { if (c.name.equals(name)) { @@ -840,7 +864,7 @@ class Field extends Element { final String name; final TypeRef type; - Field(CustomClass parent, String name, JsonObject jsonElement) { + Field(TypeDefinition parent, String name, JsonObject jsonElement) { super(parent, jsonElement); this.name = name; this.type = new TypeRef(this, jsonElement.getAsJsonObject().get("type")); @@ -1151,6 +1175,43 @@ private void writeConstructor(List output, String bodyOffset) { } } +class CustomInterface extends TypeDefinition { + final String name; + final List fields = new ArrayList<>(); + + CustomInterface(Element parent, String name, JsonObject jsonElement) { + super(parent, true, jsonElement); + this.name = name; + if (jsonElement.has("properties")) { + for (JsonElement item : jsonElement.getAsJsonArray("properties")) { + JsonObject propertyJson = item.getAsJsonObject(); + fields.add(new Field(this, propertyJson.get("name").getAsString(), propertyJson)); + } + } + } + + @Override + String name() { + return name; + } + + @Override + void writeTo(List output, String offset) { + output.add(offset + "public interface " + name + " {"); + String bodyOffset = offset + " "; + boolean first = true; + for (Field f : fields) { + if (!first) { + output.add(""); + } + first = false; + writeJavadoc(output, bodyOffset, f.comment()); + output.add(bodyOffset + f.type.toJava() + " " + f.name + "();"); + } + output.add(offset + "}"); + } +} + class Enum extends TypeDefinition { final List enumValues; @@ -1199,18 +1260,25 @@ public class ApiGenerator { System.out.println("Writing assertion files to: " + dir.getCanonicalPath()); generate(api, assertionsDir, "com.microsoft.playwright.assertions", isAssertion().and(isSoftAssertion().negate()), sharedTypes); - writeTopLevelTypes(sharedTypes, optionsDir, "com.microsoft.playwright"); + writeTopLevelTypes(sharedTypes, dir, optionsDir, "com.microsoft.playwright"); } - private void writeTopLevelTypes(Map topLevelTypes, File optionsDir, String packageName) throws IOException { + private void writeTopLevelTypes(Map topLevelTypes, File dir, File optionsDir, String packageName) throws IOException { for (TypeDefinition e : topLevelTypes.values()) { List lines = new ArrayList<>(); lines.add(Interface.header); - lines.add("package " + packageName + ".options;"); + File targetDir; + if (e instanceof CustomInterface) { + lines.add("package " + packageName + ";"); + targetDir = dir; + } else { + lines.add("package " + packageName + ".options;"); + targetDir = optionsDir; + } lines.add(""); e.writeTo(lines, ""); String text = String.join("\n", lines); - try (FileWriter writer = new FileWriter(new File(optionsDir, e.name() + ".java"))) { + try (FileWriter writer = new FileWriter(new File(targetDir, e.name() + ".java"))) { writer.write(text); } }