Version
1.52.0
Steps to reproduce
Use this reproduction example, run the main method.
import com.microsoft.playwright.Browser;
import com.microsoft.playwright.BrowserContext;
import com.microsoft.playwright.Locator;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.Playwright;
import java.util.List;
public class LocatorScreenshotBugReproducer {
public static void main(String[] args) {
try (Playwright playwright = Playwright.create();
Browser browser = playwright.chromium().launch()) {
BrowserContext context = browser.newContext();
Page page = context.newPage();
page.navigate("https://www.playwright.dev");
var heading = page.locator("h1");
heading.waitFor();
// this throws exception, only if a mask is specified
heading.screenshot(
new Locator.ScreenshotOptions()
.setMask(List.of(heading.locator("span"))));
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
}
Expected behavior
Screenshot should be taken
Actual behavior
The code fails with an exception and the following stack trace:
com.google.gson.JsonIOException: Failed making field 'java.lang.Throwable#detailMessage' accessible; either increase its visibility or write a custom TypeAdapter for its declaring type.
at com.google.gson.internal.reflect.ReflectionHelper.makeAccessible(ReflectionHelper.java:38)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:286)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:130)
at com.google.gson.Gson.getAdapter(Gson.java:556)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:160)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:294)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:130)
at com.google.gson.Gson.getAdapter(Gson.java:556)
at com.google.gson.internal.bind.MapTypeAdapterFactory.create(MapTypeAdapterFactory.java:125)
at com.google.gson.Gson.getAdapter(Gson.java:556)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:160)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:294)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:130)
at com.google.gson.Gson.getAdapter(Gson.java:556)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:160)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:294)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:130)
at com.google.gson.Gson.getAdapter(Gson.java:556)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:160)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:294)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:130)
at com.google.gson.Gson.getAdapter(Gson.java:556)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:160)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:294)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:130)
at com.google.gson.Gson.getAdapter(Gson.java:556)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:160)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:294)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:130)
at com.google.gson.Gson.getAdapter(Gson.java:556)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:55)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:97)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:61)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:70)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:196)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:368)
at com.google.gson.Gson.toJson(Gson.java:842)
at com.google.gson.Gson.toJsonTree(Gson.java:712)
at com.google.gson.Gson.toJsonTree(Gson.java:689)
at com.microsoft.playwright.impl.ElementHandleImpl.screenshotImpl(ElementHandleImpl.java:349)
at com.microsoft.playwright.impl.ElementHandleImpl.lambda$screenshot$25(ElementHandleImpl.java:329)
at com.microsoft.playwright.impl.LoggingSupport.withLogging(LoggingSupport.java:47)
at com.microsoft.playwright.impl.ChannelOwner.withLogging(ChannelOwner.java:97)
at com.microsoft.playwright.impl.ElementHandleImpl.screenshot(ElementHandleImpl.java:329)
at com.microsoft.playwright.impl.LocatorImpl.lambda$screenshot$5(LocatorImpl.java:486)
at com.microsoft.playwright.impl.LocatorImpl.withElement(LocatorImpl.java:85)
at com.microsoft.playwright.impl.LocatorImpl.screenshot(LocatorImpl.java:486)
at LocatorScreenshotBugReproducer.main(LocatorScreenshotBugReproducer.java:22)
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private java.lang.String java.lang.Throwable.detailMessage accessible: module java.base does not "opens java.lang" to unnamed module @59fa0ced
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:178)
at java.base/java.lang.reflect.Field.setAccessible(Field.java:172)
at com.google.gson.internal.reflect.ReflectionHelper.makeAccessible(ReflectionHelper.java:35)
... 47 more
Additional context
Bug analysis:
The Locator screenshot functionality forgets to remove the mask field from the generic Gson serialization of the options, and Gson finally fails because deep inside the mask field (which is a Locator) is an Exception field which cannot be serialized.
The Page screenshot code has handled this by special-casing the mask field. This needs to be done also for the Locator code (actually located in ElementHandleImpl).
Working screenshot code:
|
List<Locator> mask = options.mask; |
|
options.mask = null; |
|
JsonObject params = gson().toJsonTree(options).getAsJsonObject(); |
|
options.mask = mask; |
|
params.remove("path"); |
|
if (mask != null) { |
|
JsonArray maskArray = new JsonArray(); |
|
for (Locator locator: mask) { |
|
maskArray.add(((LocatorImpl) locator).toProtocol()); |
|
} |
|
params.add("mask", maskArray); |
|
} |
Special handling for mask missing:
|
private byte[] screenshotImpl(ScreenshotOptions options) { |
Environment
Java 17
Version
1.52.0
Steps to reproduce
Use this reproduction example, run the main method.
Expected behavior
Screenshot should be taken
Actual behavior
The code fails with an exception and the following stack trace:
Additional context
Bug analysis:
The Locator screenshot functionality forgets to remove the
maskfield from the generic Gson serialization of the options, and Gson finally fails because deep inside themaskfield (which is aLocator) is anExceptionfield which cannot be serialized.The Page screenshot code has handled this by special-casing the
maskfield. This needs to be done also for the Locator code (actually located inElementHandleImpl).Working screenshot code:
playwright-java/playwright/src/main/java/com/microsoft/playwright/impl/PageImpl.java
Lines 1211 to 1222 in fddb146
Special handling for
maskmissing:playwright-java/playwright/src/main/java/com/microsoft/playwright/impl/ElementHandleImpl.java
Line 332 in fddb146
Environment
Java 17