diff --git a/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java b/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java index f53c08e2..823706b3 100644 --- a/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java +++ b/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java @@ -263,7 +263,8 @@ class Event extends Element { void writeTo(List output, String offset) { // TODO: only whitelisted events are generated for now as the API may change. - if (!"Page.console".equals(jsonPath) && + if (!"BrowserContext.page".equals(jsonPath) && + !"Page.console".equals(jsonPath) && !"Page.popup".equals(jsonPath)) { return; } diff --git a/lib/src/main/java/com/microsoft/playwright/BrowserContext.java b/lib/src/main/java/com/microsoft/playwright/BrowserContext.java index fdae8bfd..2c5e1b02 100644 --- a/lib/src/main/java/com/microsoft/playwright/BrowserContext.java +++ b/lib/src/main/java/com/microsoft/playwright/BrowserContext.java @@ -59,6 +59,7 @@ public interface BrowserContext { return this; } } + Deferred waitForPage(); void close(); void addCookies(List cookies); default void addInitScript(String script) { diff --git a/lib/src/main/java/com/microsoft/playwright/impl/BrowserContextImpl.java b/lib/src/main/java/com/microsoft/playwright/impl/BrowserContextImpl.java index bea93111..8a271951 100644 --- a/lib/src/main/java/com/microsoft/playwright/impl/BrowserContextImpl.java +++ b/lib/src/main/java/com/microsoft/playwright/impl/BrowserContextImpl.java @@ -137,8 +137,8 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext { return null; } - - public Supplier waitForPage() { + @Override + public Deferred waitForPage() { Supplier pageSupplier = waitForProtocolEvent("page"); return () -> { JsonObject params = pageSupplier.get(); @@ -146,5 +146,4 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext { return connection.getExistingObject(guid); }; } - } diff --git a/lib/src/main/java/com/microsoft/playwright/impl/FrameImpl.java b/lib/src/main/java/com/microsoft/playwright/impl/FrameImpl.java index 5bee4ae3..26aad3c9 100644 --- a/lib/src/main/java/com/microsoft/playwright/impl/FrameImpl.java +++ b/lib/src/main/java/com/microsoft/playwright/impl/FrameImpl.java @@ -23,14 +23,30 @@ import com.google.gson.JsonObject; import com.microsoft.playwright.*; import java.util.*; +import java.util.function.Supplier; +import static com.microsoft.playwright.Frame.LoadState.*; import static com.microsoft.playwright.impl.Helpers.isFunctionBody; public class FrameImpl extends ChannelOwner implements Frame { PageImpl page; + private final Set loadStates = new HashSet<>(); FrameImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) { super(parent, type, guid, initializer); + + for (JsonElement item : initializer.get("loadStates").getAsJsonArray()) { + loadStates.add(loadStateFromProtocol(item.getAsString())); + } + } + + private static LoadState loadStateFromProtocol(String value) { + switch (value) { + case "load": return LOAD; + case "domcontentloaded": return DOMCONTENTLOADED; + case "networkidle": return NETWORKIDLE; + default: throw new RuntimeException("Unexpected value: " + value); + } } private static SerializedValue serializeValue(Object value) { @@ -425,7 +441,13 @@ public class FrameImpl extends ChannelOwner implements Frame { @Override public void waitForLoadState(LoadState state, WaitForLoadStateOptions options) { - + if (state == null) { + state = LOAD; + } + while (!loadStates.contains(state)) { + // TODO: support timeout! + connection.processOneMessage(); + } } @Override @@ -442,4 +464,18 @@ public class FrameImpl extends ChannelOwner implements Frame { public void waitForTimeout(int timeout) { } + + protected void handleEvent(String event, JsonObject params) { + if ("loadstate".equals(event)) { + JsonElement add = params.get("add"); + if (add != null) { + loadStates.add(loadStateFromProtocol(add.getAsString())); + } + JsonElement remove = params.get("remove"); + if (remove != null) { + loadStates.remove(loadStateFromProtocol(remove.getAsString())); + } + } + } + } diff --git a/lib/src/main/java/com/microsoft/playwright/impl/PageImpl.java b/lib/src/main/java/com/microsoft/playwright/impl/PageImpl.java index 97a75a52..84c52900 100644 --- a/lib/src/main/java/com/microsoft/playwright/impl/PageImpl.java +++ b/lib/src/main/java/com/microsoft/playwright/impl/PageImpl.java @@ -398,7 +398,7 @@ public class PageImpl extends ChannelOwner implements Page { @Override public void waitForLoadState(LoadState state, WaitForLoadStateOptions options) { - + mainFrame.waitForLoadState(convertViaJson(state, Frame.LoadState.class), convertViaJson(options, Frame.WaitForLoadStateOptions.class)); } @Override diff --git a/lib/src/test/java/com/microsoft/playwright/Server.java b/lib/src/test/java/com/microsoft/playwright/Server.java index 3c293e3e..2ba67d99 100644 --- a/lib/src/test/java/com/microsoft/playwright/Server.java +++ b/lib/src/test/java/com/microsoft/playwright/Server.java @@ -16,17 +16,19 @@ package com.microsoft.playwright; +import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; import java.io.*; import java.net.InetSocketAddress; -import java.net.URI; import java.nio.file.FileSystems; -import java.nio.file.spi.FileTypeDetector; import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; +import static java.util.Collections.checkedCollection; import static java.util.Collections.singletonList; public class Server implements HttpHandler { @@ -40,6 +42,8 @@ public class Server implements HttpHandler { public final String EMPTY_PAGE; private final File resourcesDir; + private final Map> requestSubscribers = Collections.synchronizedMap(new HashMap<>()); + Server(int port) throws IOException { PORT = port; PREFIX = "http://localhost:" + PORT; @@ -59,9 +63,29 @@ public class Server implements HttpHandler { server.stop(0); } + + public static class Request { + // TODO: make a copy to ensure thread safety + public final Headers headers; + + public Request(Headers headers) { + this.headers = headers; + } + } + + Future waitForRequest(String path) { + CompletableFuture future = requestSubscribers.get(path); + if (future == null) { + future = new CompletableFuture<>(); + requestSubscribers.put(path, future); + } + return future; + } + @Override public void handle(HttpExchange exchange) throws IOException { - File file = new File(resourcesDir, exchange.getRequestURI().getPath().substring(1)); + String path = exchange.getRequestURI().getPath(); + File file = new File(resourcesDir, path.substring(1)); exchange.getResponseHeaders().put("Content-Type", singletonList(mimeType(file))); try (FileInputStream input = new FileInputStream(file)) { exchange.sendResponseHeaders(200, 0); @@ -73,6 +97,14 @@ public class Server implements HttpHandler { } } exchange.getResponseBody().close(); + + synchronized (requestSubscribers) { + CompletableFuture subscriber = requestSubscribers.get(path); + if (subscriber != null) { + requestSubscribers.remove(path); + subscriber.complete(new Request(exchange.getRequestHeaders())); + } + } } private static void copy(InputStream in, OutputStream out) throws IOException { diff --git a/lib/src/test/java/com/microsoft/playwright/TestPopup.java b/lib/src/test/java/com/microsoft/playwright/TestPopup.java new file mode 100644 index 00000000..20ac8ca0 --- /dev/null +++ b/lib/src/test/java/com/microsoft/playwright/TestPopup.java @@ -0,0 +1,89 @@ +/** + * 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; + +import org.junit.jupiter.api.*; + +import java.io.IOException; +import java.util.Arrays; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TestPopup { + private static Playwright playwright; + private static Server server; + private Browser browser; + private boolean isChromium; + private boolean isWebKit; + private boolean headful; + private BrowserContext context; + private Page page; + + @BeforeAll + static void createPlaywright() { + playwright = Playwright.create(); + } + + @BeforeAll + static void startServer() throws IOException { + server = new Server(8907); + } + + @AfterAll + static void stopServer() throws IOException { + server.stop(); + server = null; + } + + @BeforeEach + void setUp() { +// BrowserType.LaunchOptions options = new BrowserType.LaunchOptions().withHeadless(false).withSlowMo(1000); + BrowserType.LaunchOptions options = new BrowserType.LaunchOptions(); + browser = playwright.chromium().launch(options); + isChromium = true; + isWebKit = false; + headful = false; + context = browser.newContext(); + page = context.newPage(); + } + + @AfterEach + void tearDown() { + browser.close(); + } + + @Test + void should_inherit_user_agent_from_browser_context() throws ExecutionException, InterruptedException { + BrowserContext context = browser.newContext(new Browser.NewContextOptions().withUserAgent("hey")); + Page page = context.newPage(); + page.navigate(server.EMPTY_PAGE); + page.setContent("link"); + Future requestPromise = server.waitForRequest("/popup/popup.html"); + Deferred popupPromise = context.waitForPage(); + page.click("a"); + Page popup = popupPromise.get(); + popup.waitForLoadState(Page.LoadState.DOMCONTENTLOADED); + String userAgent = (String) popup.evaluate("() => window['initialUserAgent']"); + Server.Request request = requestPromise.get(); + context.close(); + assertEquals("hey", userAgent); + assertEquals(Arrays.asList("hey"), request.headers.get("user-agent")); + } + +}