Compare commits
84 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1432b62b84 | |||
| fd87e552f2 | |||
| 0891edd336 | |||
| 10da11cd7c | |||
| 71b5865d5e | |||
| ce54e385c5 | |||
| 7d333f994e | |||
| 617822fa18 | |||
| 86c06c4fd3 | |||
| d57c449aef | |||
| 9fe7496261 | |||
| 9b568ab4ae | |||
| e9d5d6c5cd | |||
| 2094d40ecb | |||
| 315bda9b48 | |||
| db5bb8b9c2 | |||
| 6d982db6b9 | |||
| 65fdde3e36 | |||
| 33cbcd6fc7 | |||
| 5ce2bb6527 | |||
| 7e3cec5aac | |||
| 606d9947d4 | |||
| aea9218162 | |||
| 9885f72a66 | |||
| d63a8e31c3 | |||
| 32afbc065b | |||
| 08d84cf629 | |||
| 85553f691f | |||
| 2c6aa8f8b3 | |||
| 80c11f76aa | |||
| aa853e8386 | |||
| 99890b57bc | |||
| ab5cd1c511 | |||
| b7a822ee41 | |||
| c9787e2102 | |||
| 8bfb69b93d | |||
| 8e62a47f6d | |||
| b8a774eecc | |||
| 6fb0b01a17 | |||
| 5fa5a513d2 | |||
| 5f122784f8 | |||
| f3003bad20 | |||
| 0afb42bb0f | |||
| 8db59ba9a1 | |||
| 5bf883a456 | |||
| d4ef6d6431 | |||
| ea34deeb2b | |||
| 442a577506 | |||
| fcc1d8672a | |||
| 429f2969aa | |||
| 7642097291 | |||
| bd14c560d2 | |||
| 18ca3e6ace | |||
| b77b9b0d20 | |||
| c2690f925c | |||
| f409965c8e | |||
| 566e0f90e2 | |||
| b98e920efb | |||
| 90940b23f3 | |||
| 2c9fa04a43 | |||
| eebb223bc3 | |||
| f99d6643cd | |||
| 37e5de729f | |||
| 811ca89fd4 | |||
| 6dbebaed5a | |||
| ee4f8698da | |||
| e31ea3cbe9 | |||
| b60bbc8b88 | |||
| a577e62ece | |||
| d14d35610e | |||
| 301234aa4f | |||
| 7a7edea189 | |||
| a6f25595f3 | |||
| 5af091880f | |||
| 4c165c2069 | |||
| 65df7be823 | |||
| 2c6e278167 | |||
| 68525d1426 | |||
| 1da197fdf5 | |||
| e545829bf5 | |||
| 982cc6a3bc | |||
| dc139d25d1 | |||
| 4f9d4f170f | |||
| 55344516ea |
@@ -1,5 +1,4 @@
|
||||
name: Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -9,7 +8,6 @@ on:
|
||||
branches:
|
||||
- master
|
||||
- release-*
|
||||
|
||||
jobs:
|
||||
build:
|
||||
timeout-minutes: 30
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
name: Test CLI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- release-*
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- release-*
|
||||
jobs:
|
||||
verify:
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: true
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.m2
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ${{ runner.os }}-m2
|
||||
- name: Download drivers
|
||||
run: scripts/download_driver_for_all_platforms.sh
|
||||
- name: Intall Playwright
|
||||
run: mvn install -D skipTests --no-transfer-progress
|
||||
- name: Test CLI
|
||||
run: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -f playwright/pom.xml -D exec.args=-V
|
||||
@@ -1,6 +1,9 @@
|
||||
# 🎭 [Playwright](https://playwright.dev) for Java
|
||||
|
||||
[](https://search.maven.org/search?q=com.microsoft.playwright) [](https://join.slack.com/t/playwright/shared_invite/enQtOTEyMTUxMzgxMjIwLThjMDUxZmIyNTRiMTJjNjIyMzdmZDA3MTQxZWUwZTFjZjQwNGYxZGM5MzRmNzZlMWI5ZWUyOTkzMjE5Njg1NDg)
|
||||
[](https://javadoc.io/doc/com.microsoft.playwright/playwright)
|
||||
[](https://search.maven.org/search?q=com.microsoft.playwright)
|
||||
[](https://oss.sonatype.org/content/repositories/snapshots/com/microsoft/playwright/playwright/)
|
||||
[](https://aka.ms/playwright-slack)
|
||||
|
||||
#### [Website](https://playwright.dev/) | [API reference](https://www.javadoc.io/doc/com.microsoft.playwright/playwright/latest/index.html)
|
||||
|
||||
@@ -8,9 +11,9 @@ Playwright is a Java library to automate [Chromium](https://www.chromium.org/Hom
|
||||
|
||||
| | Linux | macOS | Windows |
|
||||
| :--- | :---: | :---: | :---: |
|
||||
| Chromium <!-- GEN:chromium-version -->89.0.4344.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Chromium <!-- GEN:chromium-version -->90.0.4392.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| WebKit <!-- GEN:webkit-version -->14.1<!-- GEN:stop --> | ✅ | ✅ | ✅ |
|
||||
| Firefox <!-- GEN:firefox-version -->85.0b1<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
| Firefox <!-- GEN:firefox-version -->85.0b5<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||
|
||||
Headless execution is supported for all the browsers on all platforms. Check out [system requirements](https://playwright.dev/#?path=docs/intro.md&q=system-requirements) for details.
|
||||
|
||||
@@ -40,7 +43,7 @@ To run Playwright simply add following dependency to your Maven project:
|
||||
<dependency>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>playwright</artifactId>
|
||||
<version>0.171.0</version>
|
||||
<version>0.180.0</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
@@ -64,23 +67,22 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class PageScreenshot {
|
||||
public static void main(String[] args) throws Exception {
|
||||
Playwright playwright = Playwright.create();
|
||||
List<BrowserType> browserTypes = Arrays.asList(
|
||||
public static void main(String[] args) {
|
||||
try (Playwright playwright = Playwright.create()) {
|
||||
List<BrowserType> browserTypes = Arrays.asList(
|
||||
playwright.chromium(),
|
||||
playwright.webkit(),
|
||||
playwright.firefox()
|
||||
);
|
||||
for (BrowserType browserType : browserTypes) {
|
||||
Browser browser = browserType.launch();
|
||||
BrowserContext context = browser.newContext(
|
||||
new Browser.NewContextOptions().withViewport(800, 600));
|
||||
Page page = context.newPage();
|
||||
page.navigate("http://whatsmyuseragent.org/");
|
||||
page.screenshot(new Page.ScreenshotOptions().withPath(Paths.get("screenshot-" + browserType.name() + ".png")));
|
||||
browser.close();
|
||||
);
|
||||
for (BrowserType browserType : browserTypes) {
|
||||
try (Browser browser = browserType.launch()) {
|
||||
BrowserContext context = browser.newContext();
|
||||
Page page = context.newPage();
|
||||
page.navigate("http://whatsmyuseragent.org/");
|
||||
page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("screenshot-" + browserType.name() + ".png")));
|
||||
}
|
||||
}
|
||||
}
|
||||
playwright.close();
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -90,27 +92,31 @@ public class PageScreenshot {
|
||||
This snippet emulates Mobile Chromium on a device at a given geolocation, navigates to openstreetmap.org, performs action and takes a screenshot.
|
||||
|
||||
```java
|
||||
import com.microsoft.playwright.options.*;
|
||||
import com.microsoft.playwright.*;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
public class MobileAndGeolocation {
|
||||
public static void main(String[] args) throws Exception {
|
||||
Playwright playwright = Playwright.create();
|
||||
BrowserType browserType = playwright.chromium();
|
||||
Browser browser = browserType.launch(new BrowserType.LaunchOptions().withHeadless(false));
|
||||
DeviceDescriptor pixel2 = playwright.devices().get("Pixel 2");
|
||||
BrowserContext context = browser.newContext(new Browser.NewContextOptions()
|
||||
.withDevice(pixel2)
|
||||
.withLocale("en-US")
|
||||
.withGeolocation(new Geolocation(41.889938, 12.492507))
|
||||
.withPermissions(asList("geolocation")));
|
||||
Page page = context.newPage();
|
||||
page.navigate("https://www.openstreetmap.org/");
|
||||
page.click("a[data-original-title=\"Show My Location\"]");
|
||||
page.screenshot(new Page.ScreenshotOptions().withPath(Paths.get("colosseum-pixel2.png")));
|
||||
browser.close();
|
||||
playwright.close();
|
||||
public static void main(String[] args) {
|
||||
try (Playwright playwright = Playwright.create()) {
|
||||
Browser browser = playwright.chromium().launch();
|
||||
BrowserContext context = browser.newContext(new Browser.NewContextOptions()
|
||||
.setUserAgent("Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Mobile Safari/537.36")
|
||||
.setViewportSize(411, 731)
|
||||
.setDeviceScaleFactor(2.625)
|
||||
.setIsMobile(true)
|
||||
.setHasTouch(true)
|
||||
.setLocale("en-US")
|
||||
.setGeolocation(41.889938, 12.492507)
|
||||
.setPermissions(asList("geolocation")));
|
||||
Page page = context.newPage();
|
||||
page.navigate("https://www.openstreetmap.org/");
|
||||
page.click("a[data-original-title=\"Show My Location\"]");
|
||||
page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("colosseum-pixel2.png")));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -123,23 +129,21 @@ This code snippet navigates to example.com in Firefox, and executes a script in
|
||||
import com.microsoft.playwright.*;
|
||||
|
||||
public class EvaluateInBrowserContext {
|
||||
public static void main(String[] args) throws Exception {
|
||||
Playwright playwright = Playwright.create();
|
||||
BrowserType browserType = playwright.firefox();
|
||||
Browser browser = browserType.launch(new BrowserType.LaunchOptions().withHeadless(false));
|
||||
BrowserContext context = browser.newContext();
|
||||
Page page = context.newPage();
|
||||
page.navigate("https://www.example.com/");
|
||||
Object dimensions = page.evaluate("() => {\n" +
|
||||
public static void main(String[] args) {
|
||||
try (Playwright playwright = Playwright.create()) {
|
||||
Browser browser = playwright.firefox().launch();
|
||||
BrowserContext context = browser.newContext();
|
||||
Page page = context.newPage();
|
||||
page.navigate("https://www.example.com/");
|
||||
Object dimensions = page.evaluate("() => {\n" +
|
||||
" return {\n" +
|
||||
" width: document.documentElement.clientWidth,\n" +
|
||||
" height: document.documentElement.clientHeight,\n" +
|
||||
" deviceScaleFactor: window.devicePixelRatio\n" +
|
||||
" }\n" +
|
||||
"}");
|
||||
System.out.println(dimensions);
|
||||
browser.close();
|
||||
playwright.close();
|
||||
System.out.println(dimensions);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -152,19 +156,17 @@ This code snippet sets up request routing for a WebKit page to log all network r
|
||||
import com.microsoft.playwright.*;
|
||||
|
||||
public class InterceptNetworkRequests {
|
||||
public static void main(String[] args) throws Exception {
|
||||
Playwright playwright = Playwright.create();
|
||||
BrowserType browserType = playwright.webkit();
|
||||
Browser browser = browserType.launch();
|
||||
BrowserContext context = browser.newContext();
|
||||
Page page = context.newPage();
|
||||
page.route("**", route -> {
|
||||
System.out.println(route.request().url());
|
||||
route.continue_();
|
||||
});
|
||||
page.navigate("http://todomvc.com");
|
||||
browser.close();
|
||||
playwright.close();
|
||||
public static void main(String[] args) {
|
||||
try (Playwright playwright = Playwright.create()) {
|
||||
Browser browser = playwright.webkit().launch();
|
||||
BrowserContext context = browser.newContext();
|
||||
Page page = context.newPage();
|
||||
page.route("**", route -> {
|
||||
System.out.println(route.request().url());
|
||||
route.resume();
|
||||
});
|
||||
page.navigate("http://todomvc.com");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>parent-pom</artifactId>
|
||||
<version>0.181.0</version>
|
||||
<version>1.9.1-alpha-0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>driver-bundle</artifactId>
|
||||
|
||||
@@ -61,8 +61,13 @@ public class DriverJar extends Driver {
|
||||
// Create zip filesystem if loading from jar.
|
||||
try (FileSystem fileSystem = "jar".equals(uri.getScheme()) ? FileSystems.newFileSystem(uri, Collections.emptyMap()) : null) {
|
||||
Path srcRoot = Paths.get(uri);
|
||||
// jar file system's .relativize gives wrong results when used with
|
||||
// spring-boot-maven-plugin, convert to the default filesystem to
|
||||
// have predictable results.
|
||||
// See https://github.com/microsoft/playwright-java/issues/306
|
||||
Path srcRootDefaultFs = Paths.get(srcRoot.toString());
|
||||
Files.walk(srcRoot).forEach(fromPath -> {
|
||||
Path relative = srcRoot.relativize(fromPath);
|
||||
Path relative = srcRootDefaultFs.relativize(Paths.get(fromPath.toString()));
|
||||
Path toPath = driverTempDir.resolve(relative.toString());
|
||||
try {
|
||||
if (Files.isDirectory(fromPath)) {
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>parent-pom</artifactId>
|
||||
<version>0.181.0</version>
|
||||
<version>1.9.1-alpha-0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>driver</artifactId>
|
||||
|
||||
+9
-1
@@ -15,7 +15,7 @@
|
||||
<dependency>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>playwright</artifactId>
|
||||
<version>0.180.0-SNAPSHOT</version>
|
||||
<version>0.190.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
@@ -31,4 +31,12 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>snapshots-repo</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||
<releases><enabled>false</enabled></releases>
|
||||
<snapshots><enabled>true</enabled></snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
</project>
|
||||
|
||||
@@ -19,22 +19,20 @@ package org.example;
|
||||
import com.microsoft.playwright.*;
|
||||
|
||||
public class EvaluateInBrowserContext {
|
||||
public static void main(String[] args) throws Exception {
|
||||
Playwright playwright = Playwright.create();
|
||||
BrowserType browserType = playwright.firefox();
|
||||
Browser browser = browserType.launch();
|
||||
BrowserContext context = browser.newContext();
|
||||
Page page = context.newPage();
|
||||
page.navigate("https://www.example.com/");
|
||||
Object dimensions = page.evaluate("() => {\n" +
|
||||
" return {\n" +
|
||||
" width: document.documentElement.clientWidth,\n" +
|
||||
" height: document.documentElement.clientHeight,\n" +
|
||||
" deviceScaleFactor: window.devicePixelRatio\n" +
|
||||
" }\n" +
|
||||
"}");
|
||||
System.out.println(dimensions);
|
||||
browser.close();
|
||||
playwright.close();
|
||||
public static void main(String[] args) {
|
||||
try (Playwright playwright = Playwright.create()) {
|
||||
Browser browser = playwright.firefox().launch();
|
||||
BrowserContext context = browser.newContext();
|
||||
Page page = context.newPage();
|
||||
page.navigate("https://www.example.com/");
|
||||
Object dimensions = page.evaluate("() => {\n" +
|
||||
" return {\n" +
|
||||
" width: document.documentElement.clientWidth,\n" +
|
||||
" height: document.documentElement.clientHeight,\n" +
|
||||
" deviceScaleFactor: window.devicePixelRatio\n" +
|
||||
" }\n" +
|
||||
"}");
|
||||
System.out.println(dimensions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,18 +19,16 @@ package org.example;
|
||||
import com.microsoft.playwright.*;
|
||||
|
||||
public class InterceptNetworkRequests {
|
||||
public static void main(String[] args) throws Exception {
|
||||
Playwright playwright = Playwright.create();
|
||||
BrowserType browserType = playwright.webkit();
|
||||
Browser browser = browserType.launch();
|
||||
BrowserContext context = browser.newContext();
|
||||
Page page = context.newPage();
|
||||
page.route("**", route -> {
|
||||
System.out.println(route.request().url());
|
||||
route.continue_();
|
||||
});
|
||||
page.navigate("http://todomvc.com");
|
||||
browser.close();
|
||||
playwright.close();
|
||||
public static void main(String[] args) {
|
||||
try (Playwright playwright = Playwright.create()) {
|
||||
Browser browser = playwright.webkit().launch();
|
||||
BrowserContext context = browser.newContext();
|
||||
Page page = context.newPage();
|
||||
page.route("**", route -> {
|
||||
System.out.println(route.request().url());
|
||||
route.resume();
|
||||
});
|
||||
page.navigate("http://todomvc.com");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.example;
|
||||
|
||||
import com.microsoft.playwright.options.*;
|
||||
import com.microsoft.playwright.*;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
@@ -23,21 +24,22 @@ import java.nio.file.Paths;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
public class MobileAndGeolocation {
|
||||
public static void main(String[] args) throws Exception {
|
||||
Playwright playwright = Playwright.create();
|
||||
BrowserType browserType = playwright.chromium();
|
||||
Browser browser = browserType.launch(new BrowserType.LaunchOptions().withHeadless(false));
|
||||
DeviceDescriptor pixel2 = playwright.devices().get("Pixel 2");
|
||||
BrowserContext context = browser.newContext(new Browser.NewContextOptions()
|
||||
.withDevice(pixel2)
|
||||
.withLocale("en-US")
|
||||
.withGeolocation(new Geolocation(41.889938, 12.492507))
|
||||
.withPermissions(asList("geolocation")));
|
||||
Page page = context.newPage();
|
||||
page.navigate("https://www.openstreetmap.org/");
|
||||
page.click("a[data-original-title=\"Show My Location\"]");
|
||||
page.screenshot(new Page.ScreenshotOptions().withPath(Paths.get("colosseum-pixel2.png")));
|
||||
browser.close();
|
||||
playwright.close();
|
||||
public static void main(String[] args) {
|
||||
try (Playwright playwright = Playwright.create()) {
|
||||
Browser browser = playwright.chromium().launch();
|
||||
BrowserContext context = browser.newContext(new Browser.NewContextOptions()
|
||||
.setUserAgent("Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3765.0 Mobile Safari/537.36")
|
||||
.setViewportSize(411, 731)
|
||||
.setDeviceScaleFactor(2.625)
|
||||
.setIsMobile(true)
|
||||
.setHasTouch(true)
|
||||
.setLocale("en-US")
|
||||
.setGeolocation(41.889938, 12.492507)
|
||||
.setPermissions(asList("geolocation")));
|
||||
Page page = context.newPage();
|
||||
page.navigate("https://www.openstreetmap.org/");
|
||||
page.click("a[data-original-title=\"Show My Location\"]");
|
||||
page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("colosseum-pixel2.png")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,22 +23,21 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class PageScreenshot {
|
||||
public static void main(String[] args) throws Exception {
|
||||
Playwright playwright = Playwright.create();
|
||||
List<BrowserType> browserTypes = Arrays.asList(
|
||||
playwright.chromium(),
|
||||
playwright.webkit(),
|
||||
playwright.firefox()
|
||||
);
|
||||
for (BrowserType browserType : browserTypes) {
|
||||
Browser browser = browserType.launch();
|
||||
BrowserContext context = browser.newContext(
|
||||
new Browser.NewContextOptions().withViewport(800, 600));
|
||||
Page page = context.newPage();
|
||||
page.navigate("http://whatsmyuseragent.org/");
|
||||
page.screenshot(new Page.ScreenshotOptions().withPath(Paths.get("screenshot-" + browserType.name() + ".png")));
|
||||
browser.close();
|
||||
public static void main(String[] args) {
|
||||
try (Playwright playwright = Playwright.create()) {
|
||||
List<BrowserType> browserTypes = Arrays.asList(
|
||||
playwright.chromium(),
|
||||
playwright.webkit(),
|
||||
playwright.firefox()
|
||||
);
|
||||
for (BrowserType browserType : browserTypes) {
|
||||
try (Browser browser = browserType.launch()) {
|
||||
BrowserContext context = browser.newContext();
|
||||
Page page = context.newPage();
|
||||
page.navigate("http://whatsmyuseragent.org/");
|
||||
page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("screenshot-" + browserType.name() + ".png")));
|
||||
}
|
||||
}
|
||||
}
|
||||
playwright.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 org.example;
|
||||
|
||||
import com.microsoft.playwright.*;
|
||||
|
||||
public class PrintTitle {
|
||||
public static void main(String[] args) {
|
||||
try (Playwright playwright = Playwright.create()) {
|
||||
Browser browser = playwright.chromium().launch();
|
||||
Page page = browser.newPage();
|
||||
page.navigate("http://playwright.dev");
|
||||
System.out.println(page.title());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 org.example;
|
||||
|
||||
import com.microsoft.playwright.*;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class WebKitScreenshot {
|
||||
public static void main(String[] args) {
|
||||
try (Playwright playwright = Playwright.create()) {
|
||||
Browser browser = playwright.webkit().launch();
|
||||
Page page = browser.newPage();
|
||||
page.navigate("http://whatsmyuseragent.org/");
|
||||
page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("example.png")));
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-6
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>parent-pom</artifactId>
|
||||
<version>0.181.0</version>
|
||||
<version>1.9.1-alpha-0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>playwright</artifactId>
|
||||
@@ -61,6 +61,10 @@
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.java-websocket</groupId>
|
||||
<artifactId>Java-WebSocket</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
@@ -73,10 +77,5 @@
|
||||
<groupId>com.microsoft.playwright</groupId>
|
||||
<artifactId>driver-bundle</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.java-websocket</groupId>
|
||||
<artifactId>Java-WebSocket</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* 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 java.util.*;
|
||||
|
||||
/**
|
||||
* The Accessibility class provides methods for inspecting Chromium's accessibility tree. The accessibility tree is used by
|
||||
* assistive technology such as [screen readers](https://en.wikipedia.org/wiki/Screen_reader) or
|
||||
* [switches](https://en.wikipedia.org/wiki/Switch_access).
|
||||
*
|
||||
* <p> Accessibility is a very platform-specific thing. On different platforms, there are different screen readers that might
|
||||
* have wildly different output.
|
||||
*
|
||||
* <p> Rendering engines of Chromium, Firefox and Webkit have a concept of "accessibility tree", which is then translated into
|
||||
* different platform-specific APIs. Accessibility namespace gives access to this Accessibility Tree.
|
||||
*
|
||||
* <p> Most of the accessibility tree gets filtered out when converting from internal browser AX Tree to Platform-specific
|
||||
* AX-Tree or by assistive technologies themselves. By default, Playwright tries to approximate this filtering, exposing
|
||||
* only the "interesting" nodes of the tree.
|
||||
*/
|
||||
public interface Accessibility {
|
||||
class SnapshotOptions {
|
||||
/**
|
||||
* Prune uninteresting nodes from the tree. Defaults to {@code true}.
|
||||
*/
|
||||
public Boolean interestingOnly;
|
||||
/**
|
||||
* The root DOM element for the snapshot. Defaults to the whole page.
|
||||
*/
|
||||
public ElementHandle root;
|
||||
|
||||
public SnapshotOptions withInterestingOnly(boolean interestingOnly) {
|
||||
this.interestingOnly = interestingOnly;
|
||||
return this;
|
||||
}
|
||||
public SnapshotOptions withRoot(ElementHandle root) {
|
||||
this.root = root;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
default AccessibilityNode snapshot() {
|
||||
return snapshot(null);
|
||||
}
|
||||
/**
|
||||
* Captures the current state of the accessibility tree. The returned object represents the root accessible node of the
|
||||
* page.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> The Chromium accessibility tree contains nodes that go unused on most platforms and by most screen readers.
|
||||
* Playwright will discard them as well for an easier to process tree, unless {@code interestingOnly} is set to {@code false}.
|
||||
*/
|
||||
AccessibilityNode snapshot(SnapshotOptions options);
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* 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 java.util.List;
|
||||
|
||||
public interface AccessibilityNode {
|
||||
String role();
|
||||
String name();
|
||||
String valueString();
|
||||
Double valueNumber();
|
||||
String description();
|
||||
String keyshortcuts();
|
||||
String roledescription();
|
||||
String valuetext();
|
||||
Boolean disabled();
|
||||
Boolean expanded();
|
||||
Boolean focused();
|
||||
Boolean modal();
|
||||
Boolean multiline();
|
||||
Boolean multiselectable();
|
||||
Boolean readonly();
|
||||
Boolean required();
|
||||
Boolean selected();
|
||||
enum CheckedState { CHECKED, UNCHECKED, MIXED }
|
||||
CheckedState checked();
|
||||
enum PressedState { PRESSED, RELEASED, MIXED }
|
||||
PressedState pressed();
|
||||
Integer level();
|
||||
Double valuemin();
|
||||
Double valuemax();
|
||||
String autocomplete();
|
||||
String haspopup();
|
||||
String invalid();
|
||||
String orientation();
|
||||
List<AccessibilityNode> children();
|
||||
}
|
||||
@@ -16,134 +16,46 @@
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import com.microsoft.playwright.options.*;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* - extends: [EventEmitter]
|
||||
* A Browser is created via {@link BrowserType#launch BrowserType.launch()}. An example of using a {@code Browser} to create a
|
||||
* {@code Page}:
|
||||
* <pre>{@code
|
||||
* import com.microsoft.playwright.*;
|
||||
*
|
||||
* <p> A Browser is created via [{@code method: BrowserType.launch}]. An example of using a {@code Browser} to create a [Page]:
|
||||
* public class Example {
|
||||
* public static void main(String[] args) {
|
||||
* try (Playwright playwright = Playwright.create()) {
|
||||
* BrowserType firefox = playwright.firefox()
|
||||
* Browser browser = firefox.launch();
|
||||
* Page page = browser.newPage();
|
||||
* page.navigate('https://example.com');
|
||||
* browser.close();
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*/
|
||||
public interface Browser {
|
||||
class VideoSize {
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
public VideoSize(int width, int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public int width() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public int height() {
|
||||
return height;
|
||||
}
|
||||
}
|
||||
|
||||
public interface Browser extends AutoCloseable {
|
||||
|
||||
/**
|
||||
* Emitted when Browser gets disconnected from the browser application. This might happen because of one of the following:
|
||||
* <ul>
|
||||
* <li> Browser application is closed or crashed.</li>
|
||||
* <li> The {@link Browser#close Browser.close()} method was called.</li>
|
||||
* </ul>
|
||||
*/
|
||||
void onDisconnected(Consumer<Browser> handler);
|
||||
/**
|
||||
* Removes handler that was previously added with {@link #onDisconnected onDisconnected(handler)}.
|
||||
*/
|
||||
void offDisconnected(Consumer<Browser> handler);
|
||||
|
||||
|
||||
class NewContextOptions {
|
||||
public class Proxy {
|
||||
/**
|
||||
* Proxy to be used for all requests. HTTP and SOCKS proxies are supported, for example {@code http://myproxy.com:3128} or
|
||||
* {@code socks5://myproxy.com:3128}. Short form {@code myproxy.com:3128} is considered an HTTP proxy.
|
||||
*/
|
||||
public String server;
|
||||
/**
|
||||
* Optional coma-separated domains to bypass proxy, for example {@code ".com, chromium.org, .domain.com"}.
|
||||
*/
|
||||
public String bypass;
|
||||
/**
|
||||
* Optional username to use if HTTP proxy requires authentication.
|
||||
*/
|
||||
public String username;
|
||||
/**
|
||||
* Optional password to use if HTTP proxy requires authentication.
|
||||
*/
|
||||
public String password;
|
||||
|
||||
Proxy() {
|
||||
}
|
||||
public NewContextOptions done() {
|
||||
return NewContextOptions.this;
|
||||
}
|
||||
|
||||
public Proxy withServer(String server) {
|
||||
this.server = server;
|
||||
return this;
|
||||
}
|
||||
public Proxy withBypass(String bypass) {
|
||||
this.bypass = bypass;
|
||||
return this;
|
||||
}
|
||||
public Proxy withUsername(String username) {
|
||||
this.username = username;
|
||||
return this;
|
||||
}
|
||||
public Proxy withPassword(String password) {
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
public class RecordHar {
|
||||
/**
|
||||
* Optional setting to control whether to omit request content from the HAR. Defaults to {@code false}.
|
||||
*/
|
||||
public Boolean omitContent;
|
||||
/**
|
||||
* Path on the filesystem to write the HAR file to.
|
||||
*/
|
||||
public Path path;
|
||||
|
||||
RecordHar() {
|
||||
}
|
||||
public NewContextOptions done() {
|
||||
return NewContextOptions.this;
|
||||
}
|
||||
|
||||
public RecordHar withOmitContent(boolean omitContent) {
|
||||
this.omitContent = omitContent;
|
||||
return this;
|
||||
}
|
||||
public RecordHar withPath(Path path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
public class RecordVideo {
|
||||
/**
|
||||
* Path to the directory to put videos into.
|
||||
*/
|
||||
public Path dir;
|
||||
/**
|
||||
* Optional dimensions of the recorded videos. If not specified the size will be equal to {@code viewport}. If {@code viewport} is not
|
||||
* configured explicitly the video size defaults to 1280x720. Actual picture of each page will be scaled down if necessary
|
||||
* to fit the specified size.
|
||||
*/
|
||||
public VideoSize size;
|
||||
|
||||
RecordVideo() {
|
||||
}
|
||||
public NewContextOptions done() {
|
||||
return NewContextOptions.this;
|
||||
}
|
||||
|
||||
public RecordVideo withDir(Path dir) {
|
||||
this.dir = dir;
|
||||
return this;
|
||||
}
|
||||
public RecordVideo withSize(int width, int height) {
|
||||
this.size = new VideoSize(width, height);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Whether to automatically download all the attachments. Defaults to {@code false} where all the downloads are canceled.
|
||||
*/
|
||||
@@ -153,8 +65,8 @@ public interface Browser {
|
||||
*/
|
||||
public Boolean bypassCSP;
|
||||
/**
|
||||
* Emulates {@code 'prefers-colors-scheme'} media feature, supported values are {@code 'light'}, {@code 'dark'}, {@code 'no-preference'}. See
|
||||
* [{@code method: Page.emulateMedia}] for more details. Defaults to '{@code light}'.
|
||||
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code "no-preference"}. See
|
||||
* {@link Page#emulateMedia Page.emulateMedia()} for more details. Defaults to '{@code light}'.
|
||||
*/
|
||||
public ColorScheme colorScheme;
|
||||
/**
|
||||
@@ -171,9 +83,9 @@ public interface Browser {
|
||||
*/
|
||||
public Boolean hasTouch;
|
||||
/**
|
||||
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).
|
||||
* Credentials for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication">HTTP authentication</a>.
|
||||
*/
|
||||
public BrowserContext.HTTPCredentials httpCredentials;
|
||||
public HttpCredentials httpCredentials;
|
||||
/**
|
||||
* Whether to ignore HTTPS errors during navigation. Defaults to {@code false}.
|
||||
*/
|
||||
@@ -197,8 +109,8 @@ public interface Browser {
|
||||
*/
|
||||
public Boolean offline;
|
||||
/**
|
||||
* A list of permissions to grant to all pages in this context. See [{@code method: BrowserContext.grantPermissions}] for more
|
||||
* details.
|
||||
* A list of permissions to grant to all pages in this context. See {@link BrowserContext#grantPermissions
|
||||
* BrowserContext.grantPermissions()} for more details.
|
||||
*/
|
||||
public List<String> permissions;
|
||||
/**
|
||||
@@ -208,26 +120,38 @@ public interface Browser {
|
||||
*/
|
||||
public Proxy proxy;
|
||||
/**
|
||||
* Enables [HAR](http://www.softwareishard.com/blog/har-12-spec) recording for all pages into {@code recordHar.path} file. If not
|
||||
* specified, the HAR is not recorded. Make sure to await [{@code method: BrowserContext.close}] for the HAR to be saved.
|
||||
* Optional setting to control whether to omit request content from the HAR. Defaults to {@code false}.
|
||||
*/
|
||||
public RecordHar recordHar;
|
||||
public Boolean recordHarOmitContent;
|
||||
/**
|
||||
* Enables video recording for all pages into {@code recordVideo.dir} directory. If not specified videos are not recorded. Make
|
||||
* sure to await [{@code method: BrowserContext.close}] for videos to be saved.
|
||||
* Path on the filesystem to write the HAR file to.
|
||||
*/
|
||||
public RecordVideo recordVideo;
|
||||
public Path recordHarPath;
|
||||
/**
|
||||
* Populates context with given storage state. This method can be used to initialize context with logged-in information
|
||||
* obtained via [{@code method: BrowserContext.storageState}]. Either a path to the file with saved storage, or an object with
|
||||
* the following fields:
|
||||
* Path to the directory to put videos into.
|
||||
*/
|
||||
public Path recordVideoDir;
|
||||
/**
|
||||
* Dimensions of the recorded videos. If not specified the size will be equal to {@code viewport} scaled down to fit into
|
||||
* 800x800. If {@code viewport} is not configured explicitly the video size defaults to 800x450. Actual picture of each page will
|
||||
* be scaled down if necessary to fit the specified size.
|
||||
*/
|
||||
public RecordVideoSize recordVideoSize;
|
||||
/**
|
||||
* Populates context with given storage state. This option can be used to initialize context with logged-in information
|
||||
* obtained via {@link BrowserContext#storageState BrowserContext.storageState()}.
|
||||
*/
|
||||
public String storageState;
|
||||
/**
|
||||
* Populates context with given storage state. This option can be used to initialize context with logged-in information
|
||||
* obtained via {@link BrowserContext#storageState BrowserContext.storageState()}. Path to the file with saved storage
|
||||
* state.
|
||||
*/
|
||||
public BrowserContext.StorageState storageState;
|
||||
public Path storageStatePath;
|
||||
/**
|
||||
* Changes the timezone of the context. See
|
||||
* [ICU's metaZones.txt](https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1)
|
||||
* for a list of supported timezone IDs.
|
||||
* Changes the timezone of the context. See <a
|
||||
* href="https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1">ICU's
|
||||
* metaZones.txt</a> for a list of supported timezone IDs.
|
||||
*/
|
||||
public String timezoneId;
|
||||
/**
|
||||
@@ -237,202 +161,121 @@ public interface Browser {
|
||||
/**
|
||||
* Sets a consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default viewport.
|
||||
*/
|
||||
public Page.Viewport viewport;
|
||||
public Optional<ViewportSize> viewportSize;
|
||||
|
||||
public NewContextOptions withAcceptDownloads(boolean acceptDownloads) {
|
||||
public NewContextOptions setAcceptDownloads(boolean acceptDownloads) {
|
||||
this.acceptDownloads = acceptDownloads;
|
||||
return this;
|
||||
}
|
||||
public NewContextOptions withBypassCSP(boolean bypassCSP) {
|
||||
public NewContextOptions setBypassCSP(boolean bypassCSP) {
|
||||
this.bypassCSP = bypassCSP;
|
||||
return this;
|
||||
}
|
||||
public NewContextOptions withColorScheme(ColorScheme colorScheme) {
|
||||
public NewContextOptions setColorScheme(ColorScheme colorScheme) {
|
||||
this.colorScheme = colorScheme;
|
||||
return this;
|
||||
}
|
||||
public NewContextOptions withDeviceScaleFactor(double deviceScaleFactor) {
|
||||
public NewContextOptions setDeviceScaleFactor(double deviceScaleFactor) {
|
||||
this.deviceScaleFactor = deviceScaleFactor;
|
||||
return this;
|
||||
}
|
||||
public NewContextOptions withExtraHTTPHeaders(Map<String, String> extraHTTPHeaders) {
|
||||
public NewContextOptions setExtraHTTPHeaders(Map<String, String> extraHTTPHeaders) {
|
||||
this.extraHTTPHeaders = extraHTTPHeaders;
|
||||
return this;
|
||||
}
|
||||
public NewContextOptions withGeolocation(Geolocation geolocation) {
|
||||
public NewContextOptions setGeolocation(double latitude, double longitude) {
|
||||
return setGeolocation(new Geolocation(latitude, longitude));
|
||||
}
|
||||
public NewContextOptions setGeolocation(Geolocation geolocation) {
|
||||
this.geolocation = geolocation;
|
||||
return this;
|
||||
}
|
||||
public NewContextOptions withHasTouch(boolean hasTouch) {
|
||||
public NewContextOptions setHasTouch(boolean hasTouch) {
|
||||
this.hasTouch = hasTouch;
|
||||
return this;
|
||||
}
|
||||
public NewContextOptions withHttpCredentials(String username, String password) {
|
||||
this.httpCredentials = new BrowserContext.HTTPCredentials(username, password);
|
||||
public NewContextOptions setHttpCredentials(String username, String password) {
|
||||
return setHttpCredentials(new HttpCredentials(username, password));
|
||||
}
|
||||
public NewContextOptions setHttpCredentials(HttpCredentials httpCredentials) {
|
||||
this.httpCredentials = httpCredentials;
|
||||
return this;
|
||||
}
|
||||
public NewContextOptions withIgnoreHTTPSErrors(boolean ignoreHTTPSErrors) {
|
||||
public NewContextOptions setIgnoreHTTPSErrors(boolean ignoreHTTPSErrors) {
|
||||
this.ignoreHTTPSErrors = ignoreHTTPSErrors;
|
||||
return this;
|
||||
}
|
||||
public NewContextOptions withIsMobile(boolean isMobile) {
|
||||
public NewContextOptions setIsMobile(boolean isMobile) {
|
||||
this.isMobile = isMobile;
|
||||
return this;
|
||||
}
|
||||
public NewContextOptions withJavaScriptEnabled(boolean javaScriptEnabled) {
|
||||
public NewContextOptions setJavaScriptEnabled(boolean javaScriptEnabled) {
|
||||
this.javaScriptEnabled = javaScriptEnabled;
|
||||
return this;
|
||||
}
|
||||
public NewContextOptions withLocale(String locale) {
|
||||
public NewContextOptions setLocale(String locale) {
|
||||
this.locale = locale;
|
||||
return this;
|
||||
}
|
||||
public NewContextOptions withOffline(boolean offline) {
|
||||
public NewContextOptions setOffline(boolean offline) {
|
||||
this.offline = offline;
|
||||
return this;
|
||||
}
|
||||
public NewContextOptions withPermissions(List<String> permissions) {
|
||||
public NewContextOptions setPermissions(List<String> permissions) {
|
||||
this.permissions = permissions;
|
||||
return this;
|
||||
}
|
||||
public Proxy setProxy() {
|
||||
this.proxy = new Proxy();
|
||||
return this.proxy;
|
||||
public NewContextOptions setProxy(String server) {
|
||||
return setProxy(new Proxy(server));
|
||||
}
|
||||
public RecordHar setRecordHar() {
|
||||
this.recordHar = new RecordHar();
|
||||
return this.recordHar;
|
||||
}
|
||||
public RecordVideo setRecordVideo() {
|
||||
this.recordVideo = new RecordVideo();
|
||||
return this.recordVideo;
|
||||
}
|
||||
public NewContextOptions withStorageState(BrowserContext.StorageState storageState) {
|
||||
this.storageState = storageState;
|
||||
this.storageStatePath = null;
|
||||
public NewContextOptions setProxy(Proxy proxy) {
|
||||
this.proxy = proxy;
|
||||
return this;
|
||||
}
|
||||
public NewContextOptions withStorageState(Path storageStatePath) {
|
||||
this.storageState = null;
|
||||
public NewContextOptions setRecordHarOmitContent(boolean recordHarOmitContent) {
|
||||
this.recordHarOmitContent = recordHarOmitContent;
|
||||
return this;
|
||||
}
|
||||
public NewContextOptions setRecordHarPath(Path recordHarPath) {
|
||||
this.recordHarPath = recordHarPath;
|
||||
return this;
|
||||
}
|
||||
public NewContextOptions setRecordVideoDir(Path recordVideoDir) {
|
||||
this.recordVideoDir = recordVideoDir;
|
||||
return this;
|
||||
}
|
||||
public NewContextOptions setRecordVideoSize(int width, int height) {
|
||||
return setRecordVideoSize(new RecordVideoSize(width, height));
|
||||
}
|
||||
public NewContextOptions setRecordVideoSize(RecordVideoSize recordVideoSize) {
|
||||
this.recordVideoSize = recordVideoSize;
|
||||
return this;
|
||||
}
|
||||
public NewContextOptions setStorageState(String storageState) {
|
||||
this.storageState = storageState;
|
||||
return this;
|
||||
}
|
||||
public NewContextOptions setStorageStatePath(Path storageStatePath) {
|
||||
this.storageStatePath = storageStatePath;
|
||||
return this;
|
||||
}
|
||||
public NewContextOptions withTimezoneId(String timezoneId) {
|
||||
public NewContextOptions setTimezoneId(String timezoneId) {
|
||||
this.timezoneId = timezoneId;
|
||||
return this;
|
||||
}
|
||||
public NewContextOptions withUserAgent(String userAgent) {
|
||||
public NewContextOptions setUserAgent(String userAgent) {
|
||||
this.userAgent = userAgent;
|
||||
return this;
|
||||
}
|
||||
public NewContextOptions withViewport(int width, int height) {
|
||||
this.viewport = new Page.Viewport(width, height);
|
||||
return this;
|
||||
public NewContextOptions setViewportSize(int width, int height) {
|
||||
return setViewportSize(new ViewportSize(width, height));
|
||||
}
|
||||
public NewContextOptions withDevice(DeviceDescriptor device) {
|
||||
withViewport(device.viewport().width(), device.viewport().height());
|
||||
withUserAgent(device.userAgent());
|
||||
withDeviceScaleFactor(device.deviceScaleFactor());
|
||||
withIsMobile(device.isMobile());
|
||||
withHasTouch(device.hasTouch());
|
||||
public NewContextOptions setViewportSize(ViewportSize viewportSize) {
|
||||
this.viewportSize = Optional.ofNullable(viewportSize);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class NewPageOptions {
|
||||
public class Proxy {
|
||||
/**
|
||||
* Proxy to be used for all requests. HTTP and SOCKS proxies are supported, for example {@code http://myproxy.com:3128} or
|
||||
* {@code socks5://myproxy.com:3128}. Short form {@code myproxy.com:3128} is considered an HTTP proxy.
|
||||
*/
|
||||
public String server;
|
||||
/**
|
||||
* Optional coma-separated domains to bypass proxy, for example {@code ".com, chromium.org, .domain.com"}.
|
||||
*/
|
||||
public String bypass;
|
||||
/**
|
||||
* Optional username to use if HTTP proxy requires authentication.
|
||||
*/
|
||||
public String username;
|
||||
/**
|
||||
* Optional password to use if HTTP proxy requires authentication.
|
||||
*/
|
||||
public String password;
|
||||
|
||||
Proxy() {
|
||||
}
|
||||
public NewPageOptions done() {
|
||||
return NewPageOptions.this;
|
||||
}
|
||||
|
||||
public Proxy withServer(String server) {
|
||||
this.server = server;
|
||||
return this;
|
||||
}
|
||||
public Proxy withBypass(String bypass) {
|
||||
this.bypass = bypass;
|
||||
return this;
|
||||
}
|
||||
public Proxy withUsername(String username) {
|
||||
this.username = username;
|
||||
return this;
|
||||
}
|
||||
public Proxy withPassword(String password) {
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
public class RecordHar {
|
||||
/**
|
||||
* Optional setting to control whether to omit request content from the HAR. Defaults to {@code false}.
|
||||
*/
|
||||
public Boolean omitContent;
|
||||
/**
|
||||
* Path on the filesystem to write the HAR file to.
|
||||
*/
|
||||
public Path path;
|
||||
|
||||
RecordHar() {
|
||||
}
|
||||
public NewPageOptions done() {
|
||||
return NewPageOptions.this;
|
||||
}
|
||||
|
||||
public RecordHar withOmitContent(boolean omitContent) {
|
||||
this.omitContent = omitContent;
|
||||
return this;
|
||||
}
|
||||
public RecordHar withPath(Path path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
public class RecordVideo {
|
||||
/**
|
||||
* Path to the directory to put videos into.
|
||||
*/
|
||||
public Path dir;
|
||||
/**
|
||||
* Optional dimensions of the recorded videos. If not specified the size will be equal to {@code viewport}. If {@code viewport} is not
|
||||
* configured explicitly the video size defaults to 1280x720. Actual picture of each page will be scaled down if necessary
|
||||
* to fit the specified size.
|
||||
*/
|
||||
public VideoSize size;
|
||||
|
||||
RecordVideo() {
|
||||
}
|
||||
public NewPageOptions done() {
|
||||
return NewPageOptions.this;
|
||||
}
|
||||
|
||||
public RecordVideo withDir(Path dir) {
|
||||
this.dir = dir;
|
||||
return this;
|
||||
}
|
||||
public RecordVideo withSize(int width, int height) {
|
||||
this.size = new VideoSize(width, height);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Whether to automatically download all the attachments. Defaults to {@code false} where all the downloads are canceled.
|
||||
*/
|
||||
@@ -442,8 +285,8 @@ public interface Browser {
|
||||
*/
|
||||
public Boolean bypassCSP;
|
||||
/**
|
||||
* Emulates {@code 'prefers-colors-scheme'} media feature, supported values are {@code 'light'}, {@code 'dark'}, {@code 'no-preference'}. See
|
||||
* [{@code method: Page.emulateMedia}] for more details. Defaults to '{@code light}'.
|
||||
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code "no-preference"}. See
|
||||
* {@link Page#emulateMedia Page.emulateMedia()} for more details. Defaults to '{@code light}'.
|
||||
*/
|
||||
public ColorScheme colorScheme;
|
||||
/**
|
||||
@@ -460,9 +303,9 @@ public interface Browser {
|
||||
*/
|
||||
public Boolean hasTouch;
|
||||
/**
|
||||
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).
|
||||
* Credentials for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication">HTTP authentication</a>.
|
||||
*/
|
||||
public BrowserContext.HTTPCredentials httpCredentials;
|
||||
public HttpCredentials httpCredentials;
|
||||
/**
|
||||
* Whether to ignore HTTPS errors during navigation. Defaults to {@code false}.
|
||||
*/
|
||||
@@ -486,8 +329,8 @@ public interface Browser {
|
||||
*/
|
||||
public Boolean offline;
|
||||
/**
|
||||
* A list of permissions to grant to all pages in this context. See [{@code method: BrowserContext.grantPermissions}] for more
|
||||
* details.
|
||||
* A list of permissions to grant to all pages in this context. See {@link BrowserContext#grantPermissions
|
||||
* BrowserContext.grantPermissions()} for more details.
|
||||
*/
|
||||
public List<String> permissions;
|
||||
/**
|
||||
@@ -497,26 +340,38 @@ public interface Browser {
|
||||
*/
|
||||
public Proxy proxy;
|
||||
/**
|
||||
* Enables [HAR](http://www.softwareishard.com/blog/har-12-spec) recording for all pages into {@code recordHar.path} file. If not
|
||||
* specified, the HAR is not recorded. Make sure to await [{@code method: BrowserContext.close}] for the HAR to be saved.
|
||||
* Optional setting to control whether to omit request content from the HAR. Defaults to {@code false}.
|
||||
*/
|
||||
public RecordHar recordHar;
|
||||
public Boolean recordHarOmitContent;
|
||||
/**
|
||||
* Enables video recording for all pages into {@code recordVideo.dir} directory. If not specified videos are not recorded. Make
|
||||
* sure to await [{@code method: BrowserContext.close}] for videos to be saved.
|
||||
* Path on the filesystem to write the HAR file to.
|
||||
*/
|
||||
public RecordVideo recordVideo;
|
||||
public Path recordHarPath;
|
||||
/**
|
||||
* Populates context with given storage state. This method can be used to initialize context with logged-in information
|
||||
* obtained via [{@code method: BrowserContext.storageState}]. Either a path to the file with saved storage, or an object with
|
||||
* the following fields:
|
||||
* Path to the directory to put videos into.
|
||||
*/
|
||||
public Path recordVideoDir;
|
||||
/**
|
||||
* Dimensions of the recorded videos. If not specified the size will be equal to {@code viewport} scaled down to fit into
|
||||
* 800x800. If {@code viewport} is not configured explicitly the video size defaults to 800x450. Actual picture of each page will
|
||||
* be scaled down if necessary to fit the specified size.
|
||||
*/
|
||||
public RecordVideoSize recordVideoSize;
|
||||
/**
|
||||
* Populates context with given storage state. This option can be used to initialize context with logged-in information
|
||||
* obtained via {@link BrowserContext#storageState BrowserContext.storageState()}.
|
||||
*/
|
||||
public String storageState;
|
||||
/**
|
||||
* Populates context with given storage state. This option can be used to initialize context with logged-in information
|
||||
* obtained via {@link BrowserContext#storageState BrowserContext.storageState()}. Path to the file with saved storage
|
||||
* state.
|
||||
*/
|
||||
public BrowserContext.StorageState storageState;
|
||||
public Path storageStatePath;
|
||||
/**
|
||||
* Changes the timezone of the context. See
|
||||
* [ICU's metaZones.txt](https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1)
|
||||
* for a list of supported timezone IDs.
|
||||
* Changes the timezone of the context. See <a
|
||||
* href="https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1">ICU's
|
||||
* metaZones.txt</a> for a list of supported timezone IDs.
|
||||
*/
|
||||
public String timezoneId;
|
||||
/**
|
||||
@@ -526,110 +381,123 @@ public interface Browser {
|
||||
/**
|
||||
* Sets a consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default viewport.
|
||||
*/
|
||||
public Page.Viewport viewport;
|
||||
public Optional<ViewportSize> viewportSize;
|
||||
|
||||
public NewPageOptions withAcceptDownloads(boolean acceptDownloads) {
|
||||
public NewPageOptions setAcceptDownloads(boolean acceptDownloads) {
|
||||
this.acceptDownloads = acceptDownloads;
|
||||
return this;
|
||||
}
|
||||
public NewPageOptions withBypassCSP(boolean bypassCSP) {
|
||||
public NewPageOptions setBypassCSP(boolean bypassCSP) {
|
||||
this.bypassCSP = bypassCSP;
|
||||
return this;
|
||||
}
|
||||
public NewPageOptions withColorScheme(ColorScheme colorScheme) {
|
||||
public NewPageOptions setColorScheme(ColorScheme colorScheme) {
|
||||
this.colorScheme = colorScheme;
|
||||
return this;
|
||||
}
|
||||
public NewPageOptions withDeviceScaleFactor(double deviceScaleFactor) {
|
||||
public NewPageOptions setDeviceScaleFactor(double deviceScaleFactor) {
|
||||
this.deviceScaleFactor = deviceScaleFactor;
|
||||
return this;
|
||||
}
|
||||
public NewPageOptions withExtraHTTPHeaders(Map<String, String> extraHTTPHeaders) {
|
||||
public NewPageOptions setExtraHTTPHeaders(Map<String, String> extraHTTPHeaders) {
|
||||
this.extraHTTPHeaders = extraHTTPHeaders;
|
||||
return this;
|
||||
}
|
||||
public NewPageOptions withGeolocation(Geolocation geolocation) {
|
||||
public NewPageOptions setGeolocation(double latitude, double longitude) {
|
||||
return setGeolocation(new Geolocation(latitude, longitude));
|
||||
}
|
||||
public NewPageOptions setGeolocation(Geolocation geolocation) {
|
||||
this.geolocation = geolocation;
|
||||
return this;
|
||||
}
|
||||
public NewPageOptions withHasTouch(boolean hasTouch) {
|
||||
public NewPageOptions setHasTouch(boolean hasTouch) {
|
||||
this.hasTouch = hasTouch;
|
||||
return this;
|
||||
}
|
||||
public NewPageOptions withHttpCredentials(String username, String password) {
|
||||
this.httpCredentials = new BrowserContext.HTTPCredentials(username, password);
|
||||
public NewPageOptions setHttpCredentials(String username, String password) {
|
||||
return setHttpCredentials(new HttpCredentials(username, password));
|
||||
}
|
||||
public NewPageOptions setHttpCredentials(HttpCredentials httpCredentials) {
|
||||
this.httpCredentials = httpCredentials;
|
||||
return this;
|
||||
}
|
||||
public NewPageOptions withIgnoreHTTPSErrors(boolean ignoreHTTPSErrors) {
|
||||
public NewPageOptions setIgnoreHTTPSErrors(boolean ignoreHTTPSErrors) {
|
||||
this.ignoreHTTPSErrors = ignoreHTTPSErrors;
|
||||
return this;
|
||||
}
|
||||
public NewPageOptions withIsMobile(boolean isMobile) {
|
||||
public NewPageOptions setIsMobile(boolean isMobile) {
|
||||
this.isMobile = isMobile;
|
||||
return this;
|
||||
}
|
||||
public NewPageOptions withJavaScriptEnabled(boolean javaScriptEnabled) {
|
||||
public NewPageOptions setJavaScriptEnabled(boolean javaScriptEnabled) {
|
||||
this.javaScriptEnabled = javaScriptEnabled;
|
||||
return this;
|
||||
}
|
||||
public NewPageOptions withLocale(String locale) {
|
||||
public NewPageOptions setLocale(String locale) {
|
||||
this.locale = locale;
|
||||
return this;
|
||||
}
|
||||
public NewPageOptions withOffline(boolean offline) {
|
||||
public NewPageOptions setOffline(boolean offline) {
|
||||
this.offline = offline;
|
||||
return this;
|
||||
}
|
||||
public NewPageOptions withPermissions(List<String> permissions) {
|
||||
public NewPageOptions setPermissions(List<String> permissions) {
|
||||
this.permissions = permissions;
|
||||
return this;
|
||||
}
|
||||
public Proxy setProxy() {
|
||||
this.proxy = new Proxy();
|
||||
return this.proxy;
|
||||
public NewPageOptions setProxy(String server) {
|
||||
return setProxy(new Proxy(server));
|
||||
}
|
||||
public RecordHar setRecordHar() {
|
||||
this.recordHar = new RecordHar();
|
||||
return this.recordHar;
|
||||
}
|
||||
public RecordVideo setRecordVideo() {
|
||||
this.recordVideo = new RecordVideo();
|
||||
return this.recordVideo;
|
||||
}
|
||||
public NewPageOptions withStorageState(BrowserContext.StorageState storageState) {
|
||||
this.storageState = storageState;
|
||||
this.storageStatePath = null;
|
||||
public NewPageOptions setProxy(Proxy proxy) {
|
||||
this.proxy = proxy;
|
||||
return this;
|
||||
}
|
||||
public NewPageOptions withStorageState(Path storageStatePath) {
|
||||
this.storageState = null;
|
||||
public NewPageOptions setRecordHarOmitContent(boolean recordHarOmitContent) {
|
||||
this.recordHarOmitContent = recordHarOmitContent;
|
||||
return this;
|
||||
}
|
||||
public NewPageOptions setRecordHarPath(Path recordHarPath) {
|
||||
this.recordHarPath = recordHarPath;
|
||||
return this;
|
||||
}
|
||||
public NewPageOptions setRecordVideoDir(Path recordVideoDir) {
|
||||
this.recordVideoDir = recordVideoDir;
|
||||
return this;
|
||||
}
|
||||
public NewPageOptions setRecordVideoSize(int width, int height) {
|
||||
return setRecordVideoSize(new RecordVideoSize(width, height));
|
||||
}
|
||||
public NewPageOptions setRecordVideoSize(RecordVideoSize recordVideoSize) {
|
||||
this.recordVideoSize = recordVideoSize;
|
||||
return this;
|
||||
}
|
||||
public NewPageOptions setStorageState(String storageState) {
|
||||
this.storageState = storageState;
|
||||
return this;
|
||||
}
|
||||
public NewPageOptions setStorageStatePath(Path storageStatePath) {
|
||||
this.storageStatePath = storageStatePath;
|
||||
return this;
|
||||
}
|
||||
public NewPageOptions withTimezoneId(String timezoneId) {
|
||||
public NewPageOptions setTimezoneId(String timezoneId) {
|
||||
this.timezoneId = timezoneId;
|
||||
return this;
|
||||
}
|
||||
public NewPageOptions withUserAgent(String userAgent) {
|
||||
public NewPageOptions setUserAgent(String userAgent) {
|
||||
this.userAgent = userAgent;
|
||||
return this;
|
||||
}
|
||||
public NewPageOptions withViewport(int width, int height) {
|
||||
this.viewport = new Page.Viewport(width, height);
|
||||
return this;
|
||||
public NewPageOptions setViewportSize(int width, int height) {
|
||||
return setViewportSize(new ViewportSize(width, height));
|
||||
}
|
||||
public NewPageOptions withDevice(DeviceDescriptor device) {
|
||||
withViewport(device.viewport().width(), device.viewport().height());
|
||||
withUserAgent(device.userAgent());
|
||||
withDeviceScaleFactor(device.deviceScaleFactor());
|
||||
withIsMobile(device.isMobile());
|
||||
withHasTouch(device.hasTouch());
|
||||
public NewPageOptions setViewportSize(ViewportSize viewportSize) {
|
||||
this.viewportSize = Optional.ofNullable(viewportSize);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* In case this browser is obtained using [{@code method: BrowserType.launch}], closes the browser and all of its pages (if any
|
||||
* were opened).
|
||||
* In case this browser is obtained using {@link BrowserType#launch BrowserType.launch()}, closes the browser and all of
|
||||
* its pages (if any were opened).
|
||||
*
|
||||
* <p> In case this browser is connected to, clears all created contexts belonging to this browser and disconnects from the
|
||||
* browser server.
|
||||
@@ -639,19 +507,51 @@ public interface Browser {
|
||||
void close();
|
||||
/**
|
||||
* Returns an array of all open browser contexts. In a newly created browser, this will return zero browser contexts.
|
||||
* <pre>{@code
|
||||
* Browser browser = pw.webkit().launch();
|
||||
* System.out.println(browser.contexts().size()); // prints "0"
|
||||
* BrowserContext context = browser.newContext();
|
||||
* System.out.println(browser.contexts().size()); // prints "1"
|
||||
* }</pre>
|
||||
*/
|
||||
List<BrowserContext> contexts();
|
||||
/**
|
||||
* Indicates that the browser is connected.
|
||||
*/
|
||||
boolean isConnected();
|
||||
/**
|
||||
* Creates a new browser context. It won't share cookies/cache with other browser contexts.
|
||||
* <pre>{@code
|
||||
* Browser browser = playwright.firefox().launch(); // Or 'chromium' or 'webkit'.
|
||||
* // Create a new incognito browser context.
|
||||
* BrowserContext context = browser.newContext();
|
||||
* // Create a new page in a pristine context.
|
||||
* Page page = context.newPage();
|
||||
* page.navigate('https://example.com');
|
||||
* }</pre>
|
||||
*/
|
||||
default BrowserContext newContext() {
|
||||
return newContext(null);
|
||||
}
|
||||
/**
|
||||
* Creates a new browser context. It won't share cookies/cache with other browser contexts.
|
||||
* <pre>{@code
|
||||
* Browser browser = playwright.firefox().launch(); // Or 'chromium' or 'webkit'.
|
||||
* // Create a new incognito browser context.
|
||||
* BrowserContext context = browser.newContext();
|
||||
* // Create a new page in a pristine context.
|
||||
* Page page = context.newPage();
|
||||
* page.navigate('https://example.com');
|
||||
* }</pre>
|
||||
*/
|
||||
BrowserContext newContext(NewContextOptions options);
|
||||
/**
|
||||
* Creates a new page in a new browser context. Closing this page will close the context as well.
|
||||
*
|
||||
* <p> This is a convenience API that should only be used for the single-page scenarios and short snippets. Production code and
|
||||
* testing frameworks should explicitly create {@link Browser#newContext Browser.newContext()} followed by the {@link
|
||||
* BrowserContext#newPage BrowserContext.newPage()} to control their exact life times.
|
||||
*/
|
||||
default Page newPage() {
|
||||
return newPage(null);
|
||||
}
|
||||
@@ -659,8 +559,8 @@ public interface Browser {
|
||||
* Creates a new page in a new browser context. Closing this page will close the context as well.
|
||||
*
|
||||
* <p> This is a convenience API that should only be used for the single-page scenarios and short snippets. Production code and
|
||||
* testing frameworks should explicitly create [{@code method: Browser.newContext}] followed by the
|
||||
* [{@code method: BrowserContext.newPage}] to control their exact life times.
|
||||
* testing frameworks should explicitly create {@link Browser#newContext Browser.newContext()} followed by the {@link
|
||||
* BrowserContext#newPage BrowserContext.newPage()} to control their exact life times.
|
||||
*/
|
||||
Page newPage(NewPageOptions options);
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import com.microsoft.playwright.options.*;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
@@ -23,202 +24,63 @@ import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* - extends: [EventEmitter]
|
||||
*
|
||||
* <p> BrowserContexts provide a way to operate multiple independent browser sessions.
|
||||
* BrowserContexts provide a way to operate multiple independent browser sessions.
|
||||
*
|
||||
* <p> If a page opens another page, e.g. with a {@code window.open} call, the popup will belong to the parent page's browser
|
||||
* context.
|
||||
*
|
||||
* <p> Playwright allows creation of "incognito" browser contexts with {@code browser.newContext()} method. "Incognito" browser
|
||||
* contexts don't write any browsing data to disk.
|
||||
* <pre>{@code
|
||||
* // Create a new incognito browser context
|
||||
* BrowserContext context = browser.newContext();
|
||||
* // Create a new page inside context.
|
||||
* Page page = context.newPage();
|
||||
* page.navigate("https://example.com");
|
||||
* // Dispose context once it"s no longer needed.
|
||||
* context.close();
|
||||
* }</pre>
|
||||
*/
|
||||
public interface BrowserContext {
|
||||
enum SameSite { STRICT, LAX, NONE }
|
||||
|
||||
class HTTPCredentials {
|
||||
private final String username;
|
||||
private final String password;
|
||||
|
||||
public HTTPCredentials(String username, String password) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String username() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String password() {
|
||||
return password;
|
||||
}
|
||||
}
|
||||
|
||||
class StorageState {
|
||||
public List<AddCookie> cookies;
|
||||
public List<OriginState> origins;
|
||||
|
||||
public static class OriginState {
|
||||
public final String origin;
|
||||
public List<LocalStorageItem> localStorage;
|
||||
|
||||
public static class LocalStorageItem {
|
||||
public String name;
|
||||
public String value;
|
||||
public LocalStorageItem(String name, String value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public OriginState(String origin) {
|
||||
this.origin = origin;
|
||||
}
|
||||
|
||||
public OriginState withLocalStorage(List<LocalStorageItem> localStorage) {
|
||||
this.localStorage = localStorage;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public StorageState() {
|
||||
cookies = new ArrayList<>();
|
||||
origins = new ArrayList<>();
|
||||
}
|
||||
|
||||
public List<AddCookie> cookies() {
|
||||
return this.cookies;
|
||||
}
|
||||
public List<OriginState> origins() {
|
||||
return this.origins;
|
||||
}
|
||||
}
|
||||
|
||||
public interface BrowserContext extends AutoCloseable {
|
||||
|
||||
/**
|
||||
* Emitted when Browser context gets closed. This might happen because of one of the following:
|
||||
* <ul>
|
||||
* <li> Browser context is closed.</li>
|
||||
* <li> Browser application is closed or crashed.</li>
|
||||
* <li> The {@link Browser#close Browser.close()} method was called.</li>
|
||||
* </ul>
|
||||
*/
|
||||
void onClose(Consumer<BrowserContext> handler);
|
||||
/**
|
||||
* Removes handler that was previously added with {@link #onClose onClose(handler)}.
|
||||
*/
|
||||
void offClose(Consumer<BrowserContext> handler);
|
||||
|
||||
/**
|
||||
* The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event will
|
||||
* also fire for popup pages. See also {@link Page#onPopup Page.onPopup()} to receive events about popups relevant to a
|
||||
* specific page.
|
||||
*
|
||||
* <p> The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a
|
||||
* popup with {@code window.open('http://example.com')}, this event will fire when the network request to "http://example.com" is
|
||||
* done and its response has started loading in the popup.
|
||||
* <pre>{@code
|
||||
* Page newPage = context.waitForPage(() -> {
|
||||
* page.click("a[target=_blank]");
|
||||
* });
|
||||
* System.out.println(newPage.evaluate("location.href"));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Use {@link Page#waitForLoadState Page.waitForLoadState()} to wait until the page gets to a particular state (you should
|
||||
* not need it in most cases).
|
||||
*/
|
||||
void onPage(Consumer<Page> handler);
|
||||
/**
|
||||
* Removes handler that was previously added with {@link #onPage onPage(handler)}.
|
||||
*/
|
||||
void offPage(Consumer<Page> handler);
|
||||
|
||||
|
||||
class WaitForPageOptions {
|
||||
public Double timeout;
|
||||
public WaitForPageOptions withTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
Page waitForPage(Runnable code, WaitForPageOptions options);
|
||||
default Page waitForPage(Runnable code) { return waitForPage(code, null); }
|
||||
|
||||
class AddCookie {
|
||||
public String name;
|
||||
public String value;
|
||||
/**
|
||||
* either url or domain / path are required. Optional.
|
||||
*/
|
||||
public String url;
|
||||
/**
|
||||
* either url or domain / path are required Optional.
|
||||
*/
|
||||
public String domain;
|
||||
/**
|
||||
* either url or domain / path are required Optional.
|
||||
*/
|
||||
public String path;
|
||||
/**
|
||||
* Unix time in seconds. Optional.
|
||||
*/
|
||||
public Double expires;
|
||||
/**
|
||||
* Optional.
|
||||
*/
|
||||
public Boolean httpOnly;
|
||||
/**
|
||||
* Optional.
|
||||
*/
|
||||
public Boolean secure;
|
||||
/**
|
||||
* Optional.
|
||||
*/
|
||||
public SameSite sameSite;
|
||||
|
||||
public AddCookie withName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
public AddCookie withValue(String value) {
|
||||
this.value = value;
|
||||
return this;
|
||||
}
|
||||
public AddCookie withUrl(String url) {
|
||||
this.url = url;
|
||||
return this;
|
||||
}
|
||||
public AddCookie withDomain(String domain) {
|
||||
this.domain = domain;
|
||||
return this;
|
||||
}
|
||||
public AddCookie withPath(String path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
public AddCookie withExpires(double expires) {
|
||||
this.expires = expires;
|
||||
return this;
|
||||
}
|
||||
public AddCookie withHttpOnly(boolean httpOnly) {
|
||||
this.httpOnly = httpOnly;
|
||||
return this;
|
||||
}
|
||||
public AddCookie withSecure(boolean secure) {
|
||||
this.secure = secure;
|
||||
return this;
|
||||
}
|
||||
public AddCookie withSameSite(SameSite sameSite) {
|
||||
this.sameSite = sameSite;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class Cookie {
|
||||
private String name;
|
||||
private String value;
|
||||
private String domain;
|
||||
private String path;
|
||||
/**
|
||||
* Unix time in seconds.
|
||||
*/
|
||||
private double expires;
|
||||
private boolean httpOnly;
|
||||
private boolean secure;
|
||||
private SameSite sameSite;
|
||||
|
||||
public String name() {
|
||||
return this.name;
|
||||
}
|
||||
public String value() {
|
||||
return this.value;
|
||||
}
|
||||
public String domain() {
|
||||
return this.domain;
|
||||
}
|
||||
public String path() {
|
||||
return this.path;
|
||||
}
|
||||
public double expires() {
|
||||
return this.expires;
|
||||
}
|
||||
public boolean httpOnly() {
|
||||
return this.httpOnly;
|
||||
}
|
||||
public boolean secure() {
|
||||
return this.secure;
|
||||
}
|
||||
public SameSite sameSite() {
|
||||
return this.sameSite;
|
||||
}
|
||||
}
|
||||
class ExposeBindingOptions {
|
||||
/**
|
||||
* Whether to pass the argument as a handle, instead of passing by value. When passing a handle, only one argument is
|
||||
@@ -226,7 +88,7 @@ public interface BrowserContext {
|
||||
*/
|
||||
public Boolean handle;
|
||||
|
||||
public ExposeBindingOptions withHandle(boolean handle) {
|
||||
public ExposeBindingOptions setHandle(boolean handle) {
|
||||
this.handle = handle;
|
||||
return this;
|
||||
}
|
||||
@@ -237,7 +99,7 @@ public interface BrowserContext {
|
||||
*/
|
||||
public String origin;
|
||||
|
||||
public GrantPermissionsOptions withOrigin(String origin) {
|
||||
public GrantPermissionsOptions setOrigin(String origin) {
|
||||
this.origin = origin;
|
||||
return this;
|
||||
}
|
||||
@@ -249,35 +111,85 @@ public interface BrowserContext {
|
||||
*/
|
||||
public Path path;
|
||||
|
||||
public StorageStateOptions withPath(Path path) {
|
||||
public StorageStateOptions setPath(Path path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class WaitForPageOptions {
|
||||
/**
|
||||
* Receives the {@code Page} object and resolves to truthy value when the waiting should resolve.
|
||||
*/
|
||||
public Predicate<Page> predicate;
|
||||
/**
|
||||
* Maximum time to wait for in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
|
||||
* value can be changed by using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()}.
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
public WaitForPageOptions setPredicate(Predicate<Page> predicate) {
|
||||
this.predicate = predicate;
|
||||
return this;
|
||||
}
|
||||
public WaitForPageOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Adds cookies into this browser context. All pages within this context will have these cookies installed. Cookies can be
|
||||
* obtained via [{@code method: BrowserContext.cookies}].
|
||||
* obtained via {@link BrowserContext#cookies BrowserContext.cookies()}.
|
||||
* <pre>{@code
|
||||
* browserContext.addCookies(Arrays.asList(cookieObject1, cookieObject2));
|
||||
* }</pre>
|
||||
*/
|
||||
void addCookies(List<AddCookie> cookies);
|
||||
default void addInitScript(String script) {
|
||||
addInitScript(script, null);
|
||||
}
|
||||
void addCookies(List<Cookie> cookies);
|
||||
/**
|
||||
* Adds a script which would be evaluated in one of the following scenarios:
|
||||
* - Whenever a page is created in the browser context or is navigated.
|
||||
* - Whenever a child frame is attached or navigated in any page in the browser context. In this case, the script is
|
||||
* evaluated in the context of the newly attached frame.
|
||||
* <ul>
|
||||
* <li> Whenever a page is created in the browser context or is navigated.</li>
|
||||
* <li> Whenever a child frame is attached or navigated in any page in the browser context. In this case, the script is
|
||||
* evaluated in the context of the newly attached frame.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p> The script is evaluated after the document was created but before any of its scripts were run. This is useful to amend
|
||||
* the JavaScript environment, e.g. to seed {@code Math.random}.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> The order of evaluation of multiple scripts installed via [{@code method: BrowserContext.addInitScript}] and
|
||||
* [{@code method: Page.addInitScript}] is not defined.
|
||||
* <p> An example of overriding {@code Math.random} before the page loads:
|
||||
* <pre>{@code
|
||||
* // In your playwright script, assuming the preload.js file is in same directory.
|
||||
* browserContext.addInitScript(Paths.get("preload.js"));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> The order of evaluation of multiple scripts installed via {@link BrowserContext#addInitScript
|
||||
* BrowserContext.addInitScript()} and {@link Page#addInitScript Page.addInitScript()} is not defined.
|
||||
*
|
||||
* @param script Script to be evaluated in all pages in the browser context.
|
||||
* @param arg Optional argument to pass to {@code script} (only supported when passing a function).
|
||||
*/
|
||||
void addInitScript(String script, Object arg);
|
||||
void addInitScript(String script);
|
||||
/**
|
||||
* Adds a script which would be evaluated in one of the following scenarios:
|
||||
* <ul>
|
||||
* <li> Whenever a page is created in the browser context or is navigated.</li>
|
||||
* <li> Whenever a child frame is attached or navigated in any page in the browser context. In this case, the script is
|
||||
* evaluated in the context of the newly attached frame.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p> The script is evaluated after the document was created but before any of its scripts were run. This is useful to amend
|
||||
* the JavaScript environment, e.g. to seed {@code Math.random}.
|
||||
*
|
||||
* <p> An example of overriding {@code Math.random} before the page loads:
|
||||
* <pre>{@code
|
||||
* // In your playwright script, assuming the preload.js file is in same directory.
|
||||
* browserContext.addInitScript(Paths.get("preload.js"));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> The order of evaluation of multiple scripts installed via {@link BrowserContext#addInitScript
|
||||
* BrowserContext.addInitScript()} and {@link Page#addInitScript Page.addInitScript()} is not defined.
|
||||
*
|
||||
* @param script Script to be evaluated in all pages in the browser context.
|
||||
*/
|
||||
void addInitScript(Path script);
|
||||
/**
|
||||
* Returns the browser instance of the context. If it was launched as a persistent context null gets returned.
|
||||
*/
|
||||
@@ -288,6 +200,12 @@ public interface BrowserContext {
|
||||
void clearCookies();
|
||||
/**
|
||||
* Clears all permission overrides for the browser context.
|
||||
* <pre>{@code
|
||||
* BrowserContext context = browser.newContext();
|
||||
* context.grantPermissions(Arrays.asList("clipboard-read"));
|
||||
* // do stuff ..
|
||||
* context.clearPermissions();
|
||||
* }</pre>
|
||||
*/
|
||||
void clearPermissions();
|
||||
/**
|
||||
@@ -296,8 +214,20 @@ public interface BrowserContext {
|
||||
* <p> <strong>NOTE:</strong> The default browser context cannot be closed.
|
||||
*/
|
||||
void close();
|
||||
default List<Cookie> cookies() { return cookies((List<String>) null); }
|
||||
default List<Cookie> cookies(String url) { return cookies(Arrays.asList(url)); }
|
||||
/**
|
||||
* If no URLs are specified, this method returns all cookies. If URLs are specified, only cookies that affect those URLs
|
||||
* are returned.
|
||||
*/
|
||||
default List<Cookie> cookies() {
|
||||
return cookies((String) null);
|
||||
}
|
||||
/**
|
||||
* If no URLs are specified, this method returns all cookies. If URLs are specified, only cookies that affect those URLs
|
||||
* are returned.
|
||||
*
|
||||
* @param urls Optional list of URLs.
|
||||
*/
|
||||
List<Cookie> cookies(String urls);
|
||||
/**
|
||||
* If no URLs are specified, this method returns all cookies. If URLs are specified, only cookies that affect those URLs
|
||||
* are returned.
|
||||
@@ -305,37 +235,201 @@ public interface BrowserContext {
|
||||
* @param urls Optional list of URLs.
|
||||
*/
|
||||
List<Cookie> cookies(List<String> urls);
|
||||
default void exposeBinding(String name, Page.Binding callback) {
|
||||
/**
|
||||
* The method adds a function called {@code name} on the {@code window} object of every frame in every page in the context. When
|
||||
* called, the function executes {@code callback} and returns a <a
|
||||
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a> which
|
||||
* resolves to the return value of {@code callback}. If the {@code callback} returns a <a
|
||||
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, it will be
|
||||
* awaited.
|
||||
*
|
||||
* <p> The first argument of the {@code callback} function contains information about the caller: {@code { browserContext: BrowserContext,
|
||||
* page: Page, frame: Frame }}.
|
||||
*
|
||||
* <p> See {@link Page#exposeBinding Page.exposeBinding()} for page-only version.
|
||||
*
|
||||
* <p> An example of exposing page URL to all frames in all pages in the context:
|
||||
* <pre>{@code
|
||||
* import com.microsoft.playwright.*;
|
||||
*
|
||||
* public class Example {
|
||||
* public static void main(String[] args) {
|
||||
* try (Playwright playwright = Playwright.create()) {
|
||||
* BrowserType webkit = playwright.webkit()
|
||||
* Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false));
|
||||
* BrowserContext context = browser.newContext();
|
||||
* context.exposeBinding("pageURL", (source, args) -> source.page().url());
|
||||
* Page page = context.newPage();
|
||||
* page.setContent("<script>\n" +
|
||||
* " async function onClick() {\n" +
|
||||
* " document.querySelector('div').textContent = await window.pageURL();\n" +
|
||||
* " }\n" +
|
||||
* "</script>\n" +
|
||||
* "<button onclick=\"onClick()\">Click me</button>\n" +
|
||||
* "<div></div>");
|
||||
* page.click("button");
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <p> An example of passing an element handle:
|
||||
* <pre>{@code
|
||||
* context.exposeBinding("clicked", (source, args) -> {
|
||||
* ElementHandle element = (ElementHandle) args[0];
|
||||
* System.out.println(element.textContent());
|
||||
* return null;
|
||||
* }, new BrowserContext.ExposeBindingOptions().setHandle(true));
|
||||
* page.setContent("" +
|
||||
* "<script>\n" +
|
||||
* " document.addEventListener('click', event => window.clicked(event.target));\n" +
|
||||
* "</script>\n" +
|
||||
* "<div>Click me</div>\n" +
|
||||
* "<div>Or click me</div>\n");
|
||||
* }</pre>
|
||||
*
|
||||
* @param name Name of the function on the window object.
|
||||
* @param callback Callback function that will be called in the Playwright's context.
|
||||
*/
|
||||
default void exposeBinding(String name, BindingCallback callback) {
|
||||
exposeBinding(name, callback, null);
|
||||
}
|
||||
/**
|
||||
* The method adds a function called {@code name} on the {@code window} object of every frame in every page in the context. When
|
||||
* called, the function executes {@code callback} and returns a [Promise] which resolves to the return value of {@code callback}. If
|
||||
* the {@code callback} returns a [Promise], it will be awaited.
|
||||
* called, the function executes {@code callback} and returns a <a
|
||||
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a> which
|
||||
* resolves to the return value of {@code callback}. If the {@code callback} returns a <a
|
||||
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, it will be
|
||||
* awaited.
|
||||
*
|
||||
* <p> The first argument of the {@code callback} function contains information about the caller: `{ browserContext: BrowserContext,
|
||||
* page: Page, frame: Frame }`.
|
||||
* <p> The first argument of the {@code callback} function contains information about the caller: {@code { browserContext: BrowserContext,
|
||||
* page: Page, frame: Frame }}.
|
||||
*
|
||||
* <p> See [{@code method: Page.exposeBinding}] for page-only version.
|
||||
* <p> See {@link Page#exposeBinding Page.exposeBinding()} for page-only version.
|
||||
*
|
||||
* <p> An example of exposing page URL to all frames in all pages in the context:
|
||||
* <pre>{@code
|
||||
* import com.microsoft.playwright.*;
|
||||
*
|
||||
* public class Example {
|
||||
* public static void main(String[] args) {
|
||||
* try (Playwright playwright = Playwright.create()) {
|
||||
* BrowserType webkit = playwright.webkit()
|
||||
* Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false));
|
||||
* BrowserContext context = browser.newContext();
|
||||
* context.exposeBinding("pageURL", (source, args) -> source.page().url());
|
||||
* Page page = context.newPage();
|
||||
* page.setContent("<script>\n" +
|
||||
* " async function onClick() {\n" +
|
||||
* " document.querySelector('div').textContent = await window.pageURL();\n" +
|
||||
* " }\n" +
|
||||
* "</script>\n" +
|
||||
* "<button onclick=\"onClick()\">Click me</button>\n" +
|
||||
* "<div></div>");
|
||||
* page.click("button");
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <p> An example of passing an element handle:
|
||||
* <pre>{@code
|
||||
* context.exposeBinding("clicked", (source, args) -> {
|
||||
* ElementHandle element = (ElementHandle) args[0];
|
||||
* System.out.println(element.textContent());
|
||||
* return null;
|
||||
* }, new BrowserContext.ExposeBindingOptions().setHandle(true));
|
||||
* page.setContent("" +
|
||||
* "<script>\n" +
|
||||
* " document.addEventListener('click', event => window.clicked(event.target));\n" +
|
||||
* "</script>\n" +
|
||||
* "<div>Click me</div>\n" +
|
||||
* "<div>Or click me</div>\n");
|
||||
* }</pre>
|
||||
*
|
||||
* @param name Name of the function on the window object.
|
||||
* @param callback Callback function that will be called in the Playwright's context.
|
||||
*/
|
||||
void exposeBinding(String name, Page.Binding callback, ExposeBindingOptions options);
|
||||
void exposeBinding(String name, BindingCallback callback, ExposeBindingOptions options);
|
||||
/**
|
||||
* The method adds a function called {@code name} on the {@code window} object of every frame in every page in the context. When
|
||||
* called, the function executes {@code callback} and returns a [Promise] which resolves to the return value of {@code callback}.
|
||||
* called, the function executes {@code callback} and returns a <a
|
||||
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a> which
|
||||
* resolves to the return value of {@code callback}.
|
||||
*
|
||||
* <p> If the {@code callback} returns a [Promise], it will be awaited.
|
||||
* <p> If the {@code callback} returns a <a
|
||||
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, it will be
|
||||
* awaited.
|
||||
*
|
||||
* <p> See [{@code method: Page.exposeFunction}] for page-only version.
|
||||
* <p> See {@link Page#exposeFunction Page.exposeFunction()} for page-only version.
|
||||
*
|
||||
* <p> An example of adding an {@code md5} function to all pages in the context:
|
||||
* <pre>{@code
|
||||
* import com.microsoft.playwright.*;
|
||||
*
|
||||
* import java.nio.charset.StandardCharsets;
|
||||
* import java.security.MessageDigest;
|
||||
* import java.security.NoSuchAlgorithmException;
|
||||
* import java.util.Base64;
|
||||
*
|
||||
* public class Example {
|
||||
* public static void main(String[] args) {
|
||||
* try (Playwright playwright = Playwright.create()) {
|
||||
* BrowserType webkit = playwright.webkit()
|
||||
* Browser browser = webkit.launch(new BrowserType.LaunchOptions().setHeadless(false));
|
||||
* context.exposeFunction("sha1", args -> {
|
||||
* String text = (String) args[0];
|
||||
* MessageDigest crypto;
|
||||
* try {
|
||||
* crypto = MessageDigest.getInstance("SHA-1");
|
||||
* } catch (NoSuchAlgorithmException e) {
|
||||
* return null;
|
||||
* }
|
||||
* byte[] token = crypto.digest(text.getBytes(StandardCharsets.UTF_8));
|
||||
* return Base64.getEncoder().encodeToString(token);
|
||||
* });
|
||||
* Page page = context.newPage();
|
||||
* page.setContent("<script>\n" +
|
||||
* " async function onClick() {\n" +
|
||||
* " document.querySelector('div').textContent = await window.sha1('PLAYWRIGHT');\n" +
|
||||
* " }\n" +
|
||||
* "</script>\n" +
|
||||
* "<button onclick=\"onClick()\">Click me</button>\n" +
|
||||
* "<div></div>\n");
|
||||
* page.click("button");
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @param name Name of the function on the window object.
|
||||
* @param callback Callback function that will be called in the Playwright's context.
|
||||
*/
|
||||
void exposeFunction(String name, Page.Function callback);
|
||||
void exposeFunction(String name, FunctionCallback callback);
|
||||
/**
|
||||
* Grants specified permissions to the browser context. Only grants corresponding permissions to the given origin if
|
||||
* specified.
|
||||
*
|
||||
* @param permissions A permission or an array of permissions to grant. Permissions can be one of the following values:
|
||||
* <ul>
|
||||
* <li> {@code "geolocation"}</li>
|
||||
* <li> {@code "midi"}</li>
|
||||
* <li> {@code "midi-sysex"} (system-exclusive midi)</li>
|
||||
* <li> {@code "notifications"}</li>
|
||||
* <li> {@code "push"}</li>
|
||||
* <li> {@code "camera"}</li>
|
||||
* <li> {@code "microphone"}</li>
|
||||
* <li> {@code "background-sync"}</li>
|
||||
* <li> {@code "ambient-light-sensor"}</li>
|
||||
* <li> {@code "accelerometer"}</li>
|
||||
* <li> {@code "gyroscope"}</li>
|
||||
* <li> {@code "magnetometer"}</li>
|
||||
* <li> {@code "accessibility-events"}</li>
|
||||
* <li> {@code "clipboard-read"}</li>
|
||||
* <li> {@code "clipboard-write"}</li>
|
||||
* <li> {@code "payment-handler"}</li>
|
||||
* </ul>
|
||||
*/
|
||||
default void grantPermissions(List<String> permissions) {
|
||||
grantPermissions(permissions, null);
|
||||
}
|
||||
@@ -344,22 +438,24 @@ public interface BrowserContext {
|
||||
* specified.
|
||||
*
|
||||
* @param permissions A permission or an array of permissions to grant. Permissions can be one of the following values:
|
||||
* - {@code 'geolocation'}
|
||||
* - {@code 'midi'}
|
||||
* - {@code 'midi-sysex'} (system-exclusive midi)
|
||||
* - {@code 'notifications'}
|
||||
* - {@code 'push'}
|
||||
* - {@code 'camera'}
|
||||
* - {@code 'microphone'}
|
||||
* - {@code 'background-sync'}
|
||||
* - {@code 'ambient-light-sensor'}
|
||||
* - {@code 'accelerometer'}
|
||||
* - {@code 'gyroscope'}
|
||||
* - {@code 'magnetometer'}
|
||||
* - {@code 'accessibility-events'}
|
||||
* - {@code 'clipboard-read'}
|
||||
* - {@code 'clipboard-write'}
|
||||
* - {@code 'payment-handler'}
|
||||
* <ul>
|
||||
* <li> {@code "geolocation"}</li>
|
||||
* <li> {@code "midi"}</li>
|
||||
* <li> {@code "midi-sysex"} (system-exclusive midi)</li>
|
||||
* <li> {@code "notifications"}</li>
|
||||
* <li> {@code "push"}</li>
|
||||
* <li> {@code "camera"}</li>
|
||||
* <li> {@code "microphone"}</li>
|
||||
* <li> {@code "background-sync"}</li>
|
||||
* <li> {@code "ambient-light-sensor"}</li>
|
||||
* <li> {@code "accelerometer"}</li>
|
||||
* <li> {@code "gyroscope"}</li>
|
||||
* <li> {@code "magnetometer"}</li>
|
||||
* <li> {@code "accessibility-events"}</li>
|
||||
* <li> {@code "clipboard-read"}</li>
|
||||
* <li> {@code "clipboard-write"}</li>
|
||||
* <li> {@code "payment-handler"}</li>
|
||||
* </ul>
|
||||
*/
|
||||
void grantPermissions(List<String> permissions, GrantPermissionsOptions options);
|
||||
/**
|
||||
@@ -367,20 +463,95 @@ public interface BrowserContext {
|
||||
*/
|
||||
Page newPage();
|
||||
/**
|
||||
* Returns all open pages in the context. Non visible pages, such as {@code "background_page"}, will not be listed here. You can
|
||||
* find them using [{@code method: ChromiumBrowserContext.backgroundPages}].
|
||||
* Returns all open pages in the context.
|
||||
*/
|
||||
List<Page> pages();
|
||||
/**
|
||||
* Routing provides the capability to modify network requests that are made by any page in the browser context. Once route
|
||||
* is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or aborted.
|
||||
*
|
||||
* <p> An example of a naïve handler that aborts all image requests:
|
||||
* <pre>{@code
|
||||
* BrowserContext context = browser.newContext();
|
||||
* context.route("**\/*.{png,jpg,jpeg}", route -> route.abort());
|
||||
* Page page = context.newPage();
|
||||
* page.navigate("https://example.com");
|
||||
* browser.close();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> or the same snippet using a regex pattern instead:
|
||||
* <pre>{@code
|
||||
* BrowserContext context = browser.newContext();
|
||||
* context.route(Pattern.compile("(\\.png$)|(\\.jpg$)"), route -> route.abort());
|
||||
* Page page = context.newPage();
|
||||
* page.navigate("https://example.com");
|
||||
* browser.close();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> Page routes (set up with {@link Page#route Page.route()}) take precedence over browser context routes when request
|
||||
* matches both handlers.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing.
|
||||
* @param handler handler function to route the request.
|
||||
*/
|
||||
void route(String url, Consumer<Route> handler);
|
||||
/**
|
||||
* Routing provides the capability to modify network requests that are made by any page in the browser context. Once route
|
||||
* is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or aborted.
|
||||
*
|
||||
* <p> An example of a naïve handler that aborts all image requests:
|
||||
* <pre>{@code
|
||||
* BrowserContext context = browser.newContext();
|
||||
* context.route("**\/*.{png,jpg,jpeg}", route -> route.abort());
|
||||
* Page page = context.newPage();
|
||||
* page.navigate("https://example.com");
|
||||
* browser.close();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> or the same snippet using a regex pattern instead:
|
||||
* <pre>{@code
|
||||
* BrowserContext context = browser.newContext();
|
||||
* context.route(Pattern.compile("(\\.png$)|(\\.jpg$)"), route -> route.abort());
|
||||
* Page page = context.newPage();
|
||||
* page.navigate("https://example.com");
|
||||
* browser.close();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> Page routes (set up with {@link Page#route Page.route()}) take precedence over browser context routes when request
|
||||
* matches both handlers.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] to match while routing.
|
||||
* @param handler handler function to route the request.
|
||||
*/
|
||||
void route(Pattern url, Consumer<Route> handler);
|
||||
/**
|
||||
* Routing provides the capability to modify network requests that are made by any page in the browser context. Once route
|
||||
* is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or aborted.
|
||||
*
|
||||
* <p> or the same snippet using a regex pattern instead:
|
||||
* <p> An example of a naïve handler that aborts all image requests:
|
||||
* <pre>{@code
|
||||
* BrowserContext context = browser.newContext();
|
||||
* context.route("**\/*.{png,jpg,jpeg}", route -> route.abort());
|
||||
* Page page = context.newPage();
|
||||
* page.navigate("https://example.com");
|
||||
* browser.close();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> Page routes (set up with [{@code method: Page.route}]) take precedence over browser context routes when request matches both
|
||||
* handlers.
|
||||
* <p> or the same snippet using a regex pattern instead:
|
||||
* <pre>{@code
|
||||
* BrowserContext context = browser.newContext();
|
||||
* context.route(Pattern.compile("(\\.png$)|(\\.jpg$)"), route -> route.abort());
|
||||
* Page page = context.newPage();
|
||||
* page.navigate("https://example.com");
|
||||
* browser.close();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> Page routes (set up with {@link Page#route Page.route()}) take precedence over browser context routes when request
|
||||
* matches both handlers.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Enabling routing disables http cache.
|
||||
*
|
||||
@@ -390,15 +561,18 @@ public interface BrowserContext {
|
||||
void route(Predicate<String> url, Consumer<Route> handler);
|
||||
/**
|
||||
* This setting will change the default maximum navigation time for the following methods and related shortcuts:
|
||||
* - [{@code method: Page.goBack}]
|
||||
* - [{@code method: Page.goForward}]
|
||||
* - [{@code method: Page.goto}]
|
||||
* - [{@code method: Page.reload}]
|
||||
* - [{@code method: Page.setContent}]
|
||||
* - [{@code method: Page.waitForNavigation}]
|
||||
* <ul>
|
||||
* <li> {@link Page#goBack Page.goBack()}</li>
|
||||
* <li> {@link Page#goForward Page.goForward()}</li>
|
||||
* <li> {@link Page#goto Page.goto()}</li>
|
||||
* <li> {@link Page#reload Page.reload()}</li>
|
||||
* <li> {@link Page#setContent Page.setContent()}</li>
|
||||
* <li> {@link Page#waitForNavigation Page.waitForNavigation()}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> [{@code method: Page.setDefaultNavigationTimeout}] and [{@code method: Page.setDefaultTimeout}] take priority over
|
||||
* [{@code method: BrowserContext.setDefaultNavigationTimeout}].
|
||||
* <p> <strong>NOTE:</strong> {@link Page#setDefaultNavigationTimeout Page.setDefaultNavigationTimeout()} and {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} take priority over {@link BrowserContext#setDefaultNavigationTimeout
|
||||
* BrowserContext.setDefaultNavigationTimeout()}.
|
||||
*
|
||||
* @param timeout Maximum navigation time in milliseconds
|
||||
*/
|
||||
@@ -406,27 +580,33 @@ public interface BrowserContext {
|
||||
/**
|
||||
* This setting will change the default maximum time for all the methods accepting {@code timeout} option.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> [{@code method: Page.setDefaultNavigationTimeout}], [{@code method: Page.setDefaultTimeout}] and
|
||||
* [{@code method: BrowserContext.setDefaultNavigationTimeout}] take priority over [{@code method: BrowserContext.setDefaultTimeout}].
|
||||
* <p> <strong>NOTE:</strong> {@link Page#setDefaultNavigationTimeout Page.setDefaultNavigationTimeout()}, {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} and {@link BrowserContext#setDefaultNavigationTimeout
|
||||
* BrowserContext.setDefaultNavigationTimeout()} take priority over {@link BrowserContext#setDefaultTimeout
|
||||
* BrowserContext.setDefaultTimeout()}.
|
||||
*
|
||||
* @param timeout Maximum time in milliseconds
|
||||
*/
|
||||
void setDefaultTimeout(double timeout);
|
||||
/**
|
||||
* The extra HTTP headers will be sent with every request initiated by any page in the context. These headers are merged
|
||||
* with page-specific extra HTTP headers set with [{@code method: Page.setExtraHTTPHeaders}]. If page overrides a particular
|
||||
* header, page-specific header value will be used instead of the browser context header value.
|
||||
* with page-specific extra HTTP headers set with {@link Page#setExtraHTTPHeaders Page.setExtraHTTPHeaders()}. If page
|
||||
* overrides a particular header, page-specific header value will be used instead of the browser context header value.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> [{@code method: BrowserContext.setExtraHTTPHeaders}] does not guarantee the order of headers in the outgoing requests.
|
||||
* <p> <strong>NOTE:</strong> {@link BrowserContext#setExtraHTTPHeaders BrowserContext.setExtraHTTPHeaders()} does not guarantee the order of headers
|
||||
* in the outgoing requests.
|
||||
*
|
||||
* @param headers An object containing additional HTTP headers to be sent with every request. All header values must be strings.
|
||||
*/
|
||||
void setExtraHTTPHeaders(Map<String, String> headers);
|
||||
/**
|
||||
* Sets the context's geolocation. Passing {@code null} or {@code undefined} emulates position unavailable.
|
||||
* <pre>{@code
|
||||
* browserContext.setGeolocation(new Geolocation(59.95, 30.31667));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Consider using [{@code method: BrowserContext.grantPermissions}] to grant permissions for the browser context pages to
|
||||
* read its geolocation.
|
||||
* <p> <strong>NOTE:</strong> Consider using {@link BrowserContext#grantPermissions BrowserContext.grantPermissions()} to grant permissions for the
|
||||
* browser context pages to read its geolocation.
|
||||
*/
|
||||
void setGeolocation(Geolocation geolocation);
|
||||
/**
|
||||
@@ -435,26 +615,90 @@ public interface BrowserContext {
|
||||
* @param offline Whether to emulate network being offline for the browser context.
|
||||
*/
|
||||
void setOffline(boolean offline);
|
||||
default StorageState storageState() {
|
||||
/**
|
||||
* Returns storage state for this browser context, contains current cookies and local storage snapshot.
|
||||
*/
|
||||
default String storageState() {
|
||||
return storageState(null);
|
||||
}
|
||||
/**
|
||||
* Returns storage state for this browser context, contains current cookies and local storage snapshot.
|
||||
*/
|
||||
StorageState storageState(StorageStateOptions options);
|
||||
default void unroute(String url) { unroute(url, null); }
|
||||
default void unroute(Pattern url) { unroute(url, null); }
|
||||
default void unroute(Predicate<String> url) { unroute(url, null); }
|
||||
String storageState(StorageStateOptions options);
|
||||
/**
|
||||
* Removes a route created with {@link BrowserContext#route BrowserContext.route()}. When {@code handler} is not specified,
|
||||
* removes all routes for the {@code url}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] used to register a routing with {@link BrowserContext#route
|
||||
* BrowserContext.route()}.
|
||||
*/
|
||||
default void unroute(String url) {
|
||||
unroute(url, null);
|
||||
}
|
||||
/**
|
||||
* Removes a route created with {@link BrowserContext#route BrowserContext.route()}. When {@code handler} is not specified,
|
||||
* removes all routes for the {@code url}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] used to register a routing with {@link BrowserContext#route
|
||||
* BrowserContext.route()}.
|
||||
* @param handler Optional handler function used to register a routing with {@link BrowserContext#route BrowserContext.route()}.
|
||||
*/
|
||||
void unroute(String url, Consumer<Route> handler);
|
||||
/**
|
||||
* Removes a route created with {@link BrowserContext#route BrowserContext.route()}. When {@code handler} is not specified,
|
||||
* removes all routes for the {@code url}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] used to register a routing with {@link BrowserContext#route
|
||||
* BrowserContext.route()}.
|
||||
*/
|
||||
default void unroute(Pattern url) {
|
||||
unroute(url, null);
|
||||
}
|
||||
/**
|
||||
* Removes a route created with {@link BrowserContext#route BrowserContext.route()}. When {@code handler} is not specified,
|
||||
* removes all routes for the {@code url}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] used to register a routing with {@link BrowserContext#route
|
||||
* BrowserContext.route()}.
|
||||
* @param handler Optional handler function used to register a routing with {@link BrowserContext#route BrowserContext.route()}.
|
||||
*/
|
||||
void unroute(Pattern url, Consumer<Route> handler);
|
||||
/**
|
||||
* Removes a route created with [{@code method: BrowserContext.route}]. When {@code handler} is not specified, removes all routes for
|
||||
* the {@code url}.
|
||||
* Removes a route created with {@link BrowserContext#route BrowserContext.route()}. When {@code handler} is not specified,
|
||||
* removes all routes for the {@code url}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] used to register a routing with
|
||||
* [{@code method: BrowserContext.route}].
|
||||
* @param handler Optional handler function used to register a routing with [{@code method: BrowserContext.route}].
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] used to register a routing with {@link BrowserContext#route
|
||||
* BrowserContext.route()}.
|
||||
*/
|
||||
default void unroute(Predicate<String> url) {
|
||||
unroute(url, null);
|
||||
}
|
||||
/**
|
||||
* Removes a route created with {@link BrowserContext#route BrowserContext.route()}. When {@code handler} is not specified,
|
||||
* removes all routes for the {@code url}.
|
||||
*
|
||||
* @param url A glob pattern, regex pattern or predicate receiving [URL] used to register a routing with {@link BrowserContext#route
|
||||
* BrowserContext.route()}.
|
||||
* @param handler Optional handler function used to register a routing with {@link BrowserContext#route BrowserContext.route()}.
|
||||
*/
|
||||
void unroute(Predicate<String> url, Consumer<Route> handler);
|
||||
/**
|
||||
* Performs action and waits for a new {@code Page} to be created in the context. If predicate is provided, it passes {@code Page}
|
||||
* value into the {@code predicate} function and waits for {@code predicate(event)} to return a truthy value. Will throw an error if
|
||||
* the context closes before new {@code Page} is created.
|
||||
*
|
||||
* @param callback Callback that performs the action triggering the event.
|
||||
*/
|
||||
default Page waitForPage(Runnable callback) {
|
||||
return waitForPage(null, callback);
|
||||
}
|
||||
/**
|
||||
* Performs action and waits for a new {@code Page} to be created in the context. If predicate is provided, it passes {@code Page}
|
||||
* value into the {@code predicate} function and waits for {@code predicate(event)} to return a truthy value. Will throw an error if
|
||||
* the context closes before new {@code Page} is created.
|
||||
*
|
||||
* @param callback Callback that performs the action triggering the event.
|
||||
*/
|
||||
Page waitForPage(WaitForPageOptions options, Runnable callback);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,60 +16,56 @@
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import com.microsoft.playwright.options.*;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* BrowserType provides methods to launch a specific browser instance or connect to an existing one. The following is a
|
||||
* typical example of using Playwright to drive automation:
|
||||
* <pre>{@code
|
||||
* import com.microsoft.playwright.*;
|
||||
*
|
||||
* public class Example {
|
||||
* public static void main(String[] args) {
|
||||
* try (Playwright playwright = Playwright.create()) {
|
||||
* BrowserType chromium = playwright.chromium();
|
||||
* Browser browser = chromium.launch();
|
||||
* Page page = browser.newPage();
|
||||
* page.navigate("https://example.com");
|
||||
* // other actions...
|
||||
* browser.close();
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*/
|
||||
public interface BrowserType {
|
||||
class LaunchOptions {
|
||||
public class Proxy {
|
||||
/**
|
||||
* Proxy to be used for all requests. HTTP and SOCKS proxies are supported, for example {@code http://myproxy.com:3128} or
|
||||
* {@code socks5://myproxy.com:3128}. Short form {@code myproxy.com:3128} is considered an HTTP proxy.
|
||||
*/
|
||||
public String server;
|
||||
/**
|
||||
* Optional coma-separated domains to bypass proxy, for example {@code ".com, chromium.org, .domain.com"}.
|
||||
*/
|
||||
public String bypass;
|
||||
/**
|
||||
* Optional username to use if HTTP proxy requires authentication.
|
||||
*/
|
||||
public String username;
|
||||
/**
|
||||
* Optional password to use if HTTP proxy requires authentication.
|
||||
*/
|
||||
public String password;
|
||||
|
||||
Proxy() {
|
||||
}
|
||||
public LaunchOptions done() {
|
||||
return LaunchOptions.this;
|
||||
}
|
||||
|
||||
public Proxy withServer(String server) {
|
||||
this.server = server;
|
||||
return this;
|
||||
}
|
||||
public Proxy withBypass(String bypass) {
|
||||
this.bypass = bypass;
|
||||
return this;
|
||||
}
|
||||
public Proxy withUsername(String username) {
|
||||
this.username = username;
|
||||
return this;
|
||||
}
|
||||
public Proxy withPassword(String password) {
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class ConnectOptions {
|
||||
/**
|
||||
* Additional arguments to pass to the browser instance. The list of Chromium flags can be found
|
||||
* [here](http://peter.sh/experiments/chromium-command-line-switches/).
|
||||
* Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
|
||||
* Defaults to 0.
|
||||
*/
|
||||
public Double slowMo;
|
||||
/**
|
||||
* Maximum time in milliseconds to wait for the connection to be established. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to
|
||||
* disable timeout.
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
public ConnectOptions setSlowMo(double slowMo) {
|
||||
this.slowMo = slowMo;
|
||||
return this;
|
||||
}
|
||||
public ConnectOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class LaunchOptions {
|
||||
/**
|
||||
* Additional arguments to pass to the browser instance. The list of Chromium flags can be found <a
|
||||
* href="http://peter.sh/experiments/chromium-command-line-switches/">here</a>.
|
||||
*/
|
||||
public List<String> args;
|
||||
/**
|
||||
@@ -97,8 +93,8 @@ public interface BrowserType {
|
||||
*/
|
||||
public Path executablePath;
|
||||
/**
|
||||
* Firefox user preferences. Learn more about the Firefox user preferences at
|
||||
* [{@code about:config}](https://support.mozilla.org/en-US/kb/about-config-editor-firefox).
|
||||
* Firefox user preferences. Learn more about the Firefox user preferences at <a
|
||||
* href="https://support.mozilla.org/en-US/kb/about-config-editor-firefox">{@code about:config}</a>.
|
||||
*/
|
||||
public Map<String, Object> firefoxUserPrefs;
|
||||
/**
|
||||
@@ -114,18 +110,22 @@ public interface BrowserType {
|
||||
*/
|
||||
public Boolean handleSIGTERM;
|
||||
/**
|
||||
* Whether to run browser in headless mode. More details for
|
||||
* [Chromium](https://developers.google.com/web/updates/2017/04/headless-chrome) and
|
||||
* [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode). Defaults to {@code true} unless the
|
||||
* Whether to run browser in headless mode. More details for <a
|
||||
* href="https://developers.google.com/web/updates/2017/04/headless-chrome">Chromium</a> and <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode">Firefox</a>. Defaults to {@code true} unless the
|
||||
* {@code devtools} option is {@code true}.
|
||||
*/
|
||||
public Boolean headless;
|
||||
/**
|
||||
* If {@code true}, Playwright does not pass its own configurations args and only uses the ones from {@code args}. If an array is
|
||||
* given, then filters out the given default arguments. Dangerous option; use with care. Defaults to {@code false}.
|
||||
* If {@code true}, Playwright does not pass its own configurations args and only uses the ones from {@code args}. Dangerous option;
|
||||
* use with care. Defaults to {@code false}.
|
||||
*/
|
||||
public Boolean ignoreAllDefaultArgs;
|
||||
/**
|
||||
* If {@code true}, Playwright does not pass its own configurations args and only uses the ones from {@code args}. Dangerous option;
|
||||
* use with care.
|
||||
*/
|
||||
public List<String> ignoreDefaultArgs;
|
||||
public Boolean ignoreAllDefaultArgs;
|
||||
/**
|
||||
* Network proxy settings.
|
||||
*/
|
||||
@@ -140,198 +140,82 @@ public interface BrowserType {
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
public LaunchOptions withArgs(List<String> args) {
|
||||
public LaunchOptions setArgs(List<String> args) {
|
||||
this.args = args;
|
||||
return this;
|
||||
}
|
||||
public LaunchOptions withChromiumSandbox(boolean chromiumSandbox) {
|
||||
public LaunchOptions setChromiumSandbox(boolean chromiumSandbox) {
|
||||
this.chromiumSandbox = chromiumSandbox;
|
||||
return this;
|
||||
}
|
||||
public LaunchOptions withDevtools(boolean devtools) {
|
||||
public LaunchOptions setDevtools(boolean devtools) {
|
||||
this.devtools = devtools;
|
||||
return this;
|
||||
}
|
||||
public LaunchOptions withDownloadsPath(Path downloadsPath) {
|
||||
public LaunchOptions setDownloadsPath(Path downloadsPath) {
|
||||
this.downloadsPath = downloadsPath;
|
||||
return this;
|
||||
}
|
||||
public LaunchOptions withEnv(Map<String, String> env) {
|
||||
public LaunchOptions setEnv(Map<String, String> env) {
|
||||
this.env = env;
|
||||
return this;
|
||||
}
|
||||
public LaunchOptions withExecutablePath(Path executablePath) {
|
||||
public LaunchOptions setExecutablePath(Path executablePath) {
|
||||
this.executablePath = executablePath;
|
||||
return this;
|
||||
}
|
||||
public LaunchOptions withFirefoxUserPrefs(Map<String, Object> firefoxUserPrefs) {
|
||||
public LaunchOptions setFirefoxUserPrefs(Map<String, Object> firefoxUserPrefs) {
|
||||
this.firefoxUserPrefs = firefoxUserPrefs;
|
||||
return this;
|
||||
}
|
||||
public LaunchOptions withHandleSIGHUP(boolean handleSIGHUP) {
|
||||
public LaunchOptions setHandleSIGHUP(boolean handleSIGHUP) {
|
||||
this.handleSIGHUP = handleSIGHUP;
|
||||
return this;
|
||||
}
|
||||
public LaunchOptions withHandleSIGINT(boolean handleSIGINT) {
|
||||
public LaunchOptions setHandleSIGINT(boolean handleSIGINT) {
|
||||
this.handleSIGINT = handleSIGINT;
|
||||
return this;
|
||||
}
|
||||
public LaunchOptions withHandleSIGTERM(boolean handleSIGTERM) {
|
||||
public LaunchOptions setHandleSIGTERM(boolean handleSIGTERM) {
|
||||
this.handleSIGTERM = handleSIGTERM;
|
||||
return this;
|
||||
}
|
||||
public LaunchOptions withHeadless(boolean headless) {
|
||||
public LaunchOptions setHeadless(boolean headless) {
|
||||
this.headless = headless;
|
||||
return this;
|
||||
}
|
||||
public LaunchOptions withIgnoreDefaultArgs(List<String> argumentNames) {
|
||||
this.ignoreDefaultArgs = argumentNames;
|
||||
public LaunchOptions setIgnoreAllDefaultArgs(boolean ignoreAllDefaultArgs) {
|
||||
this.ignoreAllDefaultArgs = ignoreAllDefaultArgs;
|
||||
return this;
|
||||
}
|
||||
public LaunchOptions withIgnoreAllDefaultArgs(boolean ignore) {
|
||||
this.ignoreAllDefaultArgs = ignore;
|
||||
public LaunchOptions setIgnoreDefaultArgs(List<String> ignoreDefaultArgs) {
|
||||
this.ignoreDefaultArgs = ignoreDefaultArgs;
|
||||
return this;
|
||||
}
|
||||
public Proxy setProxy() {
|
||||
this.proxy = new Proxy();
|
||||
return this.proxy;
|
||||
public LaunchOptions setProxy(String server) {
|
||||
return setProxy(new Proxy(server));
|
||||
}
|
||||
public LaunchOptions withSlowMo(double slowMo) {
|
||||
public LaunchOptions setProxy(Proxy proxy) {
|
||||
this.proxy = proxy;
|
||||
return this;
|
||||
}
|
||||
public LaunchOptions setSlowMo(double slowMo) {
|
||||
this.slowMo = slowMo;
|
||||
return this;
|
||||
}
|
||||
public LaunchOptions withTimeout(double timeout) {
|
||||
public LaunchOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class LaunchPersistentContextOptions {
|
||||
public class Proxy {
|
||||
/**
|
||||
* Proxy to be used for all requests. HTTP and SOCKS proxies are supported, for example {@code http://myproxy.com:3128} or
|
||||
* {@code socks5://myproxy.com:3128}. Short form {@code myproxy.com:3128} is considered an HTTP proxy.
|
||||
*/
|
||||
public String server;
|
||||
/**
|
||||
* Optional coma-separated domains to bypass proxy, for example {@code ".com, chromium.org, .domain.com"}.
|
||||
*/
|
||||
public String bypass;
|
||||
/**
|
||||
* Optional username to use if HTTP proxy requires authentication.
|
||||
*/
|
||||
public String username;
|
||||
/**
|
||||
* Optional password to use if HTTP proxy requires authentication.
|
||||
*/
|
||||
public String password;
|
||||
|
||||
Proxy() {
|
||||
}
|
||||
public LaunchPersistentContextOptions done() {
|
||||
return LaunchPersistentContextOptions.this;
|
||||
}
|
||||
|
||||
public Proxy withServer(String server) {
|
||||
this.server = server;
|
||||
return this;
|
||||
}
|
||||
public Proxy withBypass(String bypass) {
|
||||
this.bypass = bypass;
|
||||
return this;
|
||||
}
|
||||
public Proxy withUsername(String username) {
|
||||
this.username = username;
|
||||
return this;
|
||||
}
|
||||
public Proxy withPassword(String password) {
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
public class RecordHar {
|
||||
/**
|
||||
* Optional setting to control whether to omit request content from the HAR. Defaults to {@code false}.
|
||||
*/
|
||||
public Boolean omitContent;
|
||||
/**
|
||||
* Path on the filesystem to write the HAR file to.
|
||||
*/
|
||||
public Path path;
|
||||
|
||||
RecordHar() {
|
||||
}
|
||||
public LaunchPersistentContextOptions done() {
|
||||
return LaunchPersistentContextOptions.this;
|
||||
}
|
||||
|
||||
public RecordHar withOmitContent(boolean omitContent) {
|
||||
this.omitContent = omitContent;
|
||||
return this;
|
||||
}
|
||||
public RecordHar withPath(Path path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
public class RecordVideo {
|
||||
public class Size {
|
||||
/**
|
||||
* Video frame width.
|
||||
*/
|
||||
public int width;
|
||||
/**
|
||||
* Video frame height.
|
||||
*/
|
||||
public int height;
|
||||
|
||||
Size() {
|
||||
}
|
||||
public RecordVideo done() {
|
||||
return RecordVideo.this;
|
||||
}
|
||||
|
||||
public Size withWidth(int width) {
|
||||
this.width = width;
|
||||
return this;
|
||||
}
|
||||
public Size withHeight(int height) {
|
||||
this.height = height;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Path to the directory to put videos into.
|
||||
*/
|
||||
public Path dir;
|
||||
/**
|
||||
* Optional dimensions of the recorded videos. If not specified the size will be equal to {@code viewport}. If {@code viewport} is not
|
||||
* configured explicitly the video size defaults to 1280x720. Actual picture of each page will be scaled down if necessary
|
||||
* to fit the specified size.
|
||||
*/
|
||||
public Size size;
|
||||
|
||||
RecordVideo() {
|
||||
}
|
||||
public LaunchPersistentContextOptions done() {
|
||||
return LaunchPersistentContextOptions.this;
|
||||
}
|
||||
|
||||
public RecordVideo withDir(Path dir) {
|
||||
this.dir = dir;
|
||||
return this;
|
||||
}
|
||||
public Size setSize() {
|
||||
this.size = new Size();
|
||||
return this.size;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Whether to automatically download all the attachments. Defaults to {@code false} where all the downloads are canceled.
|
||||
*/
|
||||
public Boolean acceptDownloads;
|
||||
/**
|
||||
* Additional arguments to pass to the browser instance. The list of Chromium flags can be found
|
||||
* [here](http://peter.sh/experiments/chromium-command-line-switches/).
|
||||
* Additional arguments to pass to the browser instance. The list of Chromium flags can be found <a
|
||||
* href="http://peter.sh/experiments/chromium-command-line-switches/">here</a>.
|
||||
*/
|
||||
public List<String> args;
|
||||
/**
|
||||
@@ -343,8 +227,8 @@ public interface BrowserType {
|
||||
*/
|
||||
public Boolean chromiumSandbox;
|
||||
/**
|
||||
* Emulates {@code 'prefers-colors-scheme'} media feature, supported values are {@code 'light'}, {@code 'dark'}, {@code 'no-preference'}. See
|
||||
* [{@code method: Page.emulateMedia}] for more details. Defaults to '{@code light}'.
|
||||
* Emulates {@code "prefers-colors-scheme"} media feature, supported values are {@code "light"}, {@code "dark"}, {@code "no-preference"}. See
|
||||
* {@link Page#emulateMedia Page.emulateMedia()} for more details. Defaults to '{@code light}'.
|
||||
*/
|
||||
public ColorScheme colorScheme;
|
||||
/**
|
||||
@@ -393,22 +277,26 @@ public interface BrowserType {
|
||||
*/
|
||||
public Boolean hasTouch;
|
||||
/**
|
||||
* Whether to run browser in headless mode. More details for
|
||||
* [Chromium](https://developers.google.com/web/updates/2017/04/headless-chrome) and
|
||||
* [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode). Defaults to {@code true} unless the
|
||||
* Whether to run browser in headless mode. More details for <a
|
||||
* href="https://developers.google.com/web/updates/2017/04/headless-chrome">Chromium</a> and <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode">Firefox</a>. Defaults to {@code true} unless the
|
||||
* {@code devtools} option is {@code true}.
|
||||
*/
|
||||
public Boolean headless;
|
||||
/**
|
||||
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).
|
||||
* Credentials for <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication">HTTP authentication</a>.
|
||||
*/
|
||||
public BrowserContext.HTTPCredentials httpCredentials;
|
||||
public HttpCredentials httpCredentials;
|
||||
/**
|
||||
* If {@code true}, then do not use any of the default arguments. If an array is given, then filter out the given default
|
||||
* arguments. Dangerous option; use with care. Defaults to {@code false}.
|
||||
* If {@code true}, Playwright does not pass its own configurations args and only uses the ones from {@code args}. Dangerous option;
|
||||
* use with care. Defaults to {@code false}.
|
||||
*/
|
||||
public Boolean ignoreAllDefaultArgs;
|
||||
/**
|
||||
* If {@code true}, Playwright does not pass its own configurations args and only uses the ones from {@code args}. Dangerous option;
|
||||
* use with care.
|
||||
*/
|
||||
public List<String> ignoreDefaultArgs;
|
||||
public Boolean ignoreAllDefaultArgs;
|
||||
/**
|
||||
* Whether to ignore HTTPS errors during navigation. Defaults to {@code false}.
|
||||
*/
|
||||
@@ -432,8 +320,8 @@ public interface BrowserType {
|
||||
*/
|
||||
public Boolean offline;
|
||||
/**
|
||||
* A list of permissions to grant to all pages in this context. See [{@code method: BrowserContext.grantPermissions}] for more
|
||||
* details.
|
||||
* A list of permissions to grant to all pages in this context. See {@link BrowserContext#grantPermissions
|
||||
* BrowserContext.grantPermissions()} for more details.
|
||||
*/
|
||||
public List<String> permissions;
|
||||
/**
|
||||
@@ -441,15 +329,23 @@ public interface BrowserType {
|
||||
*/
|
||||
public Proxy proxy;
|
||||
/**
|
||||
* Enables [HAR](http://www.softwareishard.com/blog/har-12-spec) recording for all pages into {@code recordHar.path} file. If not
|
||||
* specified, the HAR is not recorded. Make sure to await [{@code method: BrowserContext.close}] for the HAR to be saved.
|
||||
* Optional setting to control whether to omit request content from the HAR. Defaults to {@code false}.
|
||||
*/
|
||||
public RecordHar recordHar;
|
||||
public Boolean recordHarOmitContent;
|
||||
/**
|
||||
* Enables video recording for all pages into {@code recordVideo.dir} directory. If not specified videos are not recorded. Make
|
||||
* sure to await [{@code method: BrowserContext.close}] for videos to be saved.
|
||||
* Path on the filesystem to write the HAR file to.
|
||||
*/
|
||||
public RecordVideo recordVideo;
|
||||
public Path recordHarPath;
|
||||
/**
|
||||
* Path to the directory to put videos into.
|
||||
*/
|
||||
public Path recordVideoDir;
|
||||
/**
|
||||
* Dimensions of the recorded videos. If not specified the size will be equal to {@code viewport} scaled down to fit into
|
||||
* 800x800. If {@code viewport} is not configured explicitly the video size defaults to 800x450. Actual picture of each page will
|
||||
* be scaled down if necessary to fit the specified size.
|
||||
*/
|
||||
public RecordVideoSize recordVideoSize;
|
||||
/**
|
||||
* Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
|
||||
* Defaults to 0.
|
||||
@@ -461,9 +357,9 @@ public interface BrowserType {
|
||||
*/
|
||||
public Double timeout;
|
||||
/**
|
||||
* Changes the timezone of the context. See
|
||||
* [ICU's metaZones.txt](https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1)
|
||||
* for a list of supported timezone IDs.
|
||||
* Changes the timezone of the context. See <a
|
||||
* href="https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1">ICU's
|
||||
* metaZones.txt</a> for a list of supported timezone IDs.
|
||||
*/
|
||||
public String timezoneId;
|
||||
/**
|
||||
@@ -473,149 +369,215 @@ public interface BrowserType {
|
||||
/**
|
||||
* Sets a consistent viewport for each page. Defaults to an 1280x720 viewport. {@code null} disables the default viewport.
|
||||
*/
|
||||
public Page.Viewport viewport;
|
||||
public Optional<ViewportSize> viewportSize;
|
||||
|
||||
public LaunchPersistentContextOptions withAcceptDownloads(boolean acceptDownloads) {
|
||||
public LaunchPersistentContextOptions setAcceptDownloads(boolean acceptDownloads) {
|
||||
this.acceptDownloads = acceptDownloads;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withArgs(List<String> args) {
|
||||
public LaunchPersistentContextOptions setArgs(List<String> args) {
|
||||
this.args = args;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withBypassCSP(boolean bypassCSP) {
|
||||
public LaunchPersistentContextOptions setBypassCSP(boolean bypassCSP) {
|
||||
this.bypassCSP = bypassCSP;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withChromiumSandbox(boolean chromiumSandbox) {
|
||||
public LaunchPersistentContextOptions setChromiumSandbox(boolean chromiumSandbox) {
|
||||
this.chromiumSandbox = chromiumSandbox;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withColorScheme(ColorScheme colorScheme) {
|
||||
public LaunchPersistentContextOptions setColorScheme(ColorScheme colorScheme) {
|
||||
this.colorScheme = colorScheme;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withDeviceScaleFactor(double deviceScaleFactor) {
|
||||
public LaunchPersistentContextOptions setDeviceScaleFactor(double deviceScaleFactor) {
|
||||
this.deviceScaleFactor = deviceScaleFactor;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withDevtools(boolean devtools) {
|
||||
public LaunchPersistentContextOptions setDevtools(boolean devtools) {
|
||||
this.devtools = devtools;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withDownloadsPath(Path downloadsPath) {
|
||||
public LaunchPersistentContextOptions setDownloadsPath(Path downloadsPath) {
|
||||
this.downloadsPath = downloadsPath;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withEnv(Map<String, String> env) {
|
||||
public LaunchPersistentContextOptions setEnv(Map<String, String> env) {
|
||||
this.env = env;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withExecutablePath(Path executablePath) {
|
||||
public LaunchPersistentContextOptions setExecutablePath(Path executablePath) {
|
||||
this.executablePath = executablePath;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withExtraHTTPHeaders(Map<String, String> extraHTTPHeaders) {
|
||||
public LaunchPersistentContextOptions setExtraHTTPHeaders(Map<String, String> extraHTTPHeaders) {
|
||||
this.extraHTTPHeaders = extraHTTPHeaders;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withGeolocation(Geolocation geolocation) {
|
||||
public LaunchPersistentContextOptions setGeolocation(double latitude, double longitude) {
|
||||
return setGeolocation(new Geolocation(latitude, longitude));
|
||||
}
|
||||
public LaunchPersistentContextOptions setGeolocation(Geolocation geolocation) {
|
||||
this.geolocation = geolocation;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withHandleSIGHUP(boolean handleSIGHUP) {
|
||||
public LaunchPersistentContextOptions setHandleSIGHUP(boolean handleSIGHUP) {
|
||||
this.handleSIGHUP = handleSIGHUP;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withHandleSIGINT(boolean handleSIGINT) {
|
||||
public LaunchPersistentContextOptions setHandleSIGINT(boolean handleSIGINT) {
|
||||
this.handleSIGINT = handleSIGINT;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withHandleSIGTERM(boolean handleSIGTERM) {
|
||||
public LaunchPersistentContextOptions setHandleSIGTERM(boolean handleSIGTERM) {
|
||||
this.handleSIGTERM = handleSIGTERM;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withHasTouch(boolean hasTouch) {
|
||||
public LaunchPersistentContextOptions setHasTouch(boolean hasTouch) {
|
||||
this.hasTouch = hasTouch;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withHeadless(boolean headless) {
|
||||
public LaunchPersistentContextOptions setHeadless(boolean headless) {
|
||||
this.headless = headless;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withHttpCredentials(String username, String password) {
|
||||
this.httpCredentials = new BrowserContext.HTTPCredentials(username, password);
|
||||
public LaunchPersistentContextOptions setHttpCredentials(String username, String password) {
|
||||
return setHttpCredentials(new HttpCredentials(username, password));
|
||||
}
|
||||
public LaunchPersistentContextOptions setHttpCredentials(HttpCredentials httpCredentials) {
|
||||
this.httpCredentials = httpCredentials;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withIgnoreDefaultArgs(List<String> argumentNames) {
|
||||
this.ignoreDefaultArgs = argumentNames;
|
||||
public LaunchPersistentContextOptions setIgnoreAllDefaultArgs(boolean ignoreAllDefaultArgs) {
|
||||
this.ignoreAllDefaultArgs = ignoreAllDefaultArgs;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withIgnoreAllDefaultArgs(boolean ignore) {
|
||||
this.ignoreAllDefaultArgs = ignore;
|
||||
public LaunchPersistentContextOptions setIgnoreDefaultArgs(List<String> ignoreDefaultArgs) {
|
||||
this.ignoreDefaultArgs = ignoreDefaultArgs;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withIgnoreHTTPSErrors(boolean ignoreHTTPSErrors) {
|
||||
public LaunchPersistentContextOptions setIgnoreHTTPSErrors(boolean ignoreHTTPSErrors) {
|
||||
this.ignoreHTTPSErrors = ignoreHTTPSErrors;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withIsMobile(boolean isMobile) {
|
||||
public LaunchPersistentContextOptions setIsMobile(boolean isMobile) {
|
||||
this.isMobile = isMobile;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withJavaScriptEnabled(boolean javaScriptEnabled) {
|
||||
public LaunchPersistentContextOptions setJavaScriptEnabled(boolean javaScriptEnabled) {
|
||||
this.javaScriptEnabled = javaScriptEnabled;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withLocale(String locale) {
|
||||
public LaunchPersistentContextOptions setLocale(String locale) {
|
||||
this.locale = locale;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withOffline(boolean offline) {
|
||||
public LaunchPersistentContextOptions setOffline(boolean offline) {
|
||||
this.offline = offline;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withPermissions(List<String> permissions) {
|
||||
public LaunchPersistentContextOptions setPermissions(List<String> permissions) {
|
||||
this.permissions = permissions;
|
||||
return this;
|
||||
}
|
||||
public Proxy setProxy() {
|
||||
this.proxy = new Proxy();
|
||||
return this.proxy;
|
||||
public LaunchPersistentContextOptions setProxy(String server) {
|
||||
return setProxy(new Proxy(server));
|
||||
}
|
||||
public RecordHar setRecordHar() {
|
||||
this.recordHar = new RecordHar();
|
||||
return this.recordHar;
|
||||
public LaunchPersistentContextOptions setProxy(Proxy proxy) {
|
||||
this.proxy = proxy;
|
||||
return this;
|
||||
}
|
||||
public RecordVideo setRecordVideo() {
|
||||
this.recordVideo = new RecordVideo();
|
||||
return this.recordVideo;
|
||||
public LaunchPersistentContextOptions setRecordHarOmitContent(boolean recordHarOmitContent) {
|
||||
this.recordHarOmitContent = recordHarOmitContent;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withSlowMo(double slowMo) {
|
||||
public LaunchPersistentContextOptions setRecordHarPath(Path recordHarPath) {
|
||||
this.recordHarPath = recordHarPath;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions setRecordVideoDir(Path recordVideoDir) {
|
||||
this.recordVideoDir = recordVideoDir;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions setRecordVideoSize(int width, int height) {
|
||||
return setRecordVideoSize(new RecordVideoSize(width, height));
|
||||
}
|
||||
public LaunchPersistentContextOptions setRecordVideoSize(RecordVideoSize recordVideoSize) {
|
||||
this.recordVideoSize = recordVideoSize;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions setSlowMo(double slowMo) {
|
||||
this.slowMo = slowMo;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withTimeout(double timeout) {
|
||||
public LaunchPersistentContextOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withTimezoneId(String timezoneId) {
|
||||
public LaunchPersistentContextOptions setTimezoneId(String timezoneId) {
|
||||
this.timezoneId = timezoneId;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withUserAgent(String userAgent) {
|
||||
public LaunchPersistentContextOptions setUserAgent(String userAgent) {
|
||||
this.userAgent = userAgent;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions withViewport(int width, int height) {
|
||||
this.viewport = new Page.Viewport(width, height);
|
||||
public LaunchPersistentContextOptions setViewportSize(int width, int height) {
|
||||
return setViewportSize(new ViewportSize(width, height));
|
||||
}
|
||||
public LaunchPersistentContextOptions setViewportSize(ViewportSize viewportSize) {
|
||||
this.viewportSize = Optional.ofNullable(viewportSize);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This methods attaches Playwright to an existing browser instance.
|
||||
*
|
||||
* @param wsEndpoint A browser websocket endpoint to connect to.
|
||||
*/
|
||||
default Browser connect(String wsEndpoint) {
|
||||
return connect(wsEndpoint, null);
|
||||
}
|
||||
/**
|
||||
* This methods attaches Playwright to an existing browser instance.
|
||||
*
|
||||
* @param wsEndpoint A browser websocket endpoint to connect to.
|
||||
*/
|
||||
Browser connect(String wsEndpoint, ConnectOptions options);
|
||||
/**
|
||||
* A path where Playwright expects to find a bundled browser executable.
|
||||
*/
|
||||
String executablePath();
|
||||
/**
|
||||
* Returns the browser instance.
|
||||
*
|
||||
* <p> You can use {@code ignoreDefaultArgs} to filter out {@code --mute-audio} from default arguments:
|
||||
* <pre>{@code
|
||||
* // Or "firefox" or "webkit".
|
||||
* Browser browser = chromium.launch(new BrowserType.LaunchOptions()
|
||||
* .setIgnoreDefaultArgs(Arrays.asList("--mute-audio")));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> > **Chromium-only** Playwright can also be used to control the Google Chrome or Microsoft Edge browsers, but it works
|
||||
* best with the version of Chromium it is bundled with. There is no guarantee it will work with any other version. Use
|
||||
* {@code executablePath} option with extreme caution.
|
||||
*
|
||||
* <p> >
|
||||
*
|
||||
* <p> > If Google Chrome (rather than Chromium) is preferred, a <a
|
||||
* href="https://www.google.com/chrome/browser/canary.html">Chrome Canary</a> or <a
|
||||
* href="https://www.chromium.org/getting-involved/dev-channel">Dev Channel</a> build is suggested.
|
||||
*
|
||||
* <p> >
|
||||
*
|
||||
* <p> > Stock browsers like Google Chrome and Microsoft Edge are suitable for tests that require proprietary media codecs for
|
||||
* video playback. See <a
|
||||
* href="https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/">this article</a> for
|
||||
* other differences between Chromium and Chrome. <a
|
||||
* href="https://chromium.googlesource.com/chromium/src/+/lkgr/docs/chromium_browser_vs_google_chrome.md">This article</a>
|
||||
* describes some differences for Linux users.
|
||||
*/
|
||||
default Browser launch() {
|
||||
return launch(null);
|
||||
}
|
||||
@@ -623,23 +585,43 @@ public interface BrowserType {
|
||||
* Returns the browser instance.
|
||||
*
|
||||
* <p> You can use {@code ignoreDefaultArgs} to filter out {@code --mute-audio} from default arguments:
|
||||
* <pre>{@code
|
||||
* // Or "firefox" or "webkit".
|
||||
* Browser browser = chromium.launch(new BrowserType.LaunchOptions()
|
||||
* .setIgnoreDefaultArgs(Arrays.asList("--mute-audio")));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> **Chromium-only** Playwright can also be used to control the Chrome browser, but it works best with the version of
|
||||
* Chromium it is bundled with. There is no guarantee it will work with any other version. Use {@code executablePath} option with
|
||||
* extreme caution.
|
||||
* >
|
||||
* If Google Chrome (rather than Chromium) is preferred, a
|
||||
* [Chrome Canary](https://www.google.com/chrome/browser/canary.html) or
|
||||
* [Dev Channel](https://www.chromium.org/getting-involved/dev-channel) build is suggested.
|
||||
* >
|
||||
* In [{@code method: BrowserType.launch}] above, any mention of Chromium also applies to Chrome.
|
||||
* >
|
||||
* See [{@code this article}](https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/) for
|
||||
* a description of the differences between Chromium and Chrome.
|
||||
* [{@code This article}](https://chromium.googlesource.com/chromium/src/+/lkgr/docs/chromium_browser_vs_google_chrome.md)
|
||||
* <p> > **Chromium-only** Playwright can also be used to control the Google Chrome or Microsoft Edge browsers, but it works
|
||||
* best with the version of Chromium it is bundled with. There is no guarantee it will work with any other version. Use
|
||||
* {@code executablePath} option with extreme caution.
|
||||
*
|
||||
* <p> >
|
||||
*
|
||||
* <p> > If Google Chrome (rather than Chromium) is preferred, a <a
|
||||
* href="https://www.google.com/chrome/browser/canary.html">Chrome Canary</a> or <a
|
||||
* href="https://www.chromium.org/getting-involved/dev-channel">Dev Channel</a> build is suggested.
|
||||
*
|
||||
* <p> >
|
||||
*
|
||||
* <p> > Stock browsers like Google Chrome and Microsoft Edge are suitable for tests that require proprietary media codecs for
|
||||
* video playback. See <a
|
||||
* href="https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/">this article</a> for
|
||||
* other differences between Chromium and Chrome. <a
|
||||
* href="https://chromium.googlesource.com/chromium/src/+/lkgr/docs/chromium_browser_vs_google_chrome.md">This article</a>
|
||||
* describes some differences for Linux users.
|
||||
*/
|
||||
Browser launch(LaunchOptions options);
|
||||
/**
|
||||
* Returns the persistent browser context instance.
|
||||
*
|
||||
* <p> Launches browser that uses persistent storage located at {@code userDataDir} and returns the only context. Closing this
|
||||
* context will automatically close the browser.
|
||||
*
|
||||
* @param userDataDir Path to a User Data Directory, which stores browser session data like cookies and local storage. More details for <a
|
||||
* href="https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md#introduction">Chromium</a> and <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#User_Profile">Firefox</a>. Note that
|
||||
* Chromium's user data directory is the **parent** directory of the "Profile Path" seen at {@code chrome://version}.
|
||||
*/
|
||||
default BrowserContext launchPersistentContext(Path userDataDir) {
|
||||
return launchPersistentContext(userDataDir, null);
|
||||
}
|
||||
@@ -649,13 +631,14 @@ public interface BrowserType {
|
||||
* <p> Launches browser that uses persistent storage located at {@code userDataDir} and returns the only context. Closing this
|
||||
* context will automatically close the browser.
|
||||
*
|
||||
* @param userDataDir Path to a User Data Directory, which stores browser session data like cookies and local storage. More details for
|
||||
* [Chromium](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md) and
|
||||
* [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#User_Profile).
|
||||
* @param userDataDir Path to a User Data Directory, which stores browser session data like cookies and local storage. More details for <a
|
||||
* href="https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md#introduction">Chromium</a> and <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#User_Profile">Firefox</a>. Note that
|
||||
* Chromium's user data directory is the **parent** directory of the "Profile Path" seen at {@code chrome://version}.
|
||||
*/
|
||||
BrowserContext launchPersistentContext(Path userDataDir, LaunchPersistentContextOptions options);
|
||||
/**
|
||||
* Returns browser name. For example: {@code 'chromium'}, {@code 'webkit'} or {@code 'firefox'}.
|
||||
* Returns browser name. For example: {@code "chromium"}, {@code "webkit"} or {@code "firefox"}.
|
||||
*/
|
||||
String name();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 com.microsoft.playwright.impl.Driver;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
/**
|
||||
* Use this class to launch playwright cli.
|
||||
*/
|
||||
public class CLI {
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
Path driver = Driver.ensureDriverInstalled();
|
||||
ProcessBuilder pb = new ProcessBuilder(driver.toString());
|
||||
pb.command().addAll(asList(args));
|
||||
if (!pb.environment().containsKey("PW_CLI_TARGET_LANG")) {
|
||||
pb.environment().put("PW_CLI_TARGET_LANG", "java");
|
||||
}
|
||||
pb.inheritIO();
|
||||
Process process = pb.start();
|
||||
System.exit(process.waitFor());
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
package com.microsoft.playwright;
|
||||
|
||||
public enum ColorScheme { DARK, LIGHT, NO_PREFERENCE }
|
||||
@@ -19,40 +19,19 @@ package com.microsoft.playwright;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* {@code ConsoleMessage} objects are dispatched by page via the [{@code event: Page.console}] event.
|
||||
* {@code ConsoleMessage} objects are dispatched by page via the {@link Page#onConsole Page.onConsole()} event.
|
||||
*/
|
||||
public interface ConsoleMessage {
|
||||
class Location {
|
||||
/**
|
||||
* URL of the resource.
|
||||
*/
|
||||
private String url;
|
||||
/**
|
||||
* 0-based line number in the resource.
|
||||
*/
|
||||
private int lineNumber;
|
||||
/**
|
||||
* 0-based column number in the resource.
|
||||
*/
|
||||
private int columnNumber;
|
||||
|
||||
public String url() {
|
||||
return this.url;
|
||||
}
|
||||
public int lineNumber() {
|
||||
return this.lineNumber;
|
||||
}
|
||||
public int columnNumber() {
|
||||
return this.columnNumber;
|
||||
}
|
||||
}
|
||||
List<JSHandle> args();
|
||||
Location location();
|
||||
/**
|
||||
* URL of the resource followed by 0-based line and column numbers in the resource formatted as {@code URL:line:column}.
|
||||
*/
|
||||
String location();
|
||||
String text();
|
||||
/**
|
||||
* One of the following values: {@code 'log'}, {@code 'debug'}, {@code 'info'}, {@code 'error'}, {@code 'warning'}, {@code 'dir'}, {@code 'dirxml'}, {@code 'table'},
|
||||
* {@code 'trace'}, {@code 'clear'}, {@code 'startGroup'}, {@code 'startGroupCollapsed'}, {@code 'endGroup'}, {@code 'assert'}, {@code 'profile'}, {@code 'profileEnd'},
|
||||
* {@code 'count'}, {@code 'timeEnd'}.
|
||||
* One of the following values: {@code "log"}, {@code "debug"}, {@code "info"}, {@code "error"}, {@code "warning"}, {@code "dir"}, {@code "dirxml"}, {@code "table"},
|
||||
* {@code "trace"}, {@code "clear"}, {@code "startGroup"}, {@code "startGroupCollapsed"}, {@code "endGroup"}, {@code "assert"}, {@code "profile"}, {@code "profileEnd"},
|
||||
* {@code "count"}, {@code "timeEnd"}.
|
||||
*/
|
||||
String type();
|
||||
}
|
||||
|
||||
@@ -19,11 +19,39 @@ package com.microsoft.playwright;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* {@code Dialog} objects are dispatched by page via the [{@code event: Page.dialog}] event.
|
||||
* {@code Dialog} objects are dispatched by page via the {@link Page#onDialog Page.onDialog()} event.
|
||||
*
|
||||
* <p> An example of using {@code Dialog} class:
|
||||
* <pre>{@code
|
||||
* import com.microsoft.playwright.*;
|
||||
*
|
||||
* public class Example {
|
||||
* public static void main(String[] args) {
|
||||
* try (Playwright playwright = Playwright.create()) {
|
||||
* BrowserType chromium = playwright.chromium();
|
||||
* Browser browser = chromium.launch();
|
||||
* Page page = browser.newPage();
|
||||
* page.onDialog(dialog -> {
|
||||
* System.out.println(dialog.message());
|
||||
* dialog.dismiss();
|
||||
* });
|
||||
* page.evaluate("alert('1')");
|
||||
* browser.close();
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Dialogs are dismissed automatically, unless there is a {@link Page#onDialog Page.onDialog()} listener. When listener is
|
||||
* present, it **must** either {@link Dialog#accept Dialog.accept()} or {@link Dialog#dismiss Dialog.dismiss()} the dialog
|
||||
* - otherwise the page will <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking">freeze</a> waiting for the
|
||||
* dialog, and actions like click will never finish.
|
||||
*/
|
||||
public interface Dialog {
|
||||
enum Type { ALERT, BEFOREUNLOAD, CONFIRM, PROMPT }
|
||||
|
||||
/**
|
||||
* Returns when the dialog has been accepted.
|
||||
*/
|
||||
default void accept() {
|
||||
accept(null);
|
||||
}
|
||||
@@ -48,6 +76,6 @@ public interface Dialog {
|
||||
/**
|
||||
* Returns dialog's type, can be one of {@code alert}, {@code beforeunload}, {@code confirm} or {@code prompt}.
|
||||
*/
|
||||
Type type();
|
||||
String type();
|
||||
}
|
||||
|
||||
|
||||
@@ -21,16 +21,30 @@ import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* {@code Download} objects are dispatched by page via the [{@code event: Page.download}] event.
|
||||
* {@code Download} objects are dispatched by page via the {@link Page#onDownload Page.onDownload()} event.
|
||||
*
|
||||
* <p> All the downloaded files belonging to the browser context are deleted when the browser context is closed. All downloaded
|
||||
* files are deleted when the browser closes.
|
||||
*
|
||||
* <p> Download event is emitted once the download starts. Download path becomes available once download completes:
|
||||
* <pre>{@code
|
||||
* // wait for download to start
|
||||
* Download download = page.waitForDownload(() -> page.click("a"));
|
||||
* // wait for download to complete
|
||||
* Path path = download.path();
|
||||
* }</pre>
|
||||
* <pre>{@code
|
||||
* // wait for download to start
|
||||
* Download download = page.waitForDownload(() -> {
|
||||
* page.click("a");
|
||||
* });
|
||||
* // wait for download to complete
|
||||
* Path path = download.path();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Browser context **must** be created with the {@code acceptDownloads} set to {@code true} when user needs access to the
|
||||
* downloaded content. If {@code acceptDownloads} is not set, download events are emitted, but the actual download is not
|
||||
* performed and user has no access to the downloaded files.
|
||||
* <p> <strong>NOTE:</strong> Browser context **must** be created with the {@code acceptDownloads} set to {@code true} when user needs access to the downloaded
|
||||
* content. If {@code acceptDownloads} is not set, download events are emitted, but the actual download is not performed and user
|
||||
* has no access to the downloaded files.
|
||||
*/
|
||||
public interface Download {
|
||||
/**
|
||||
@@ -56,10 +70,11 @@ public interface Download {
|
||||
*/
|
||||
void saveAs(Path path);
|
||||
/**
|
||||
* Returns suggested filename for this download. It is typically computed by the browser from the
|
||||
* [{@code Content-Disposition}](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) response header
|
||||
* or the {@code download} attribute. See the spec on [whatwg](https://html.spec.whatwg.org/#downloading-resources). Different
|
||||
* browsers can use different logic for computing it.
|
||||
* Returns suggested filename for this download. It is typically computed by the browser from the <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition">{@code Content-Disposition}</a> response
|
||||
* header or the {@code download} attribute. See the spec on <a
|
||||
* href="https://html.spec.whatwg.org/#downloading-resources">whatwg</a>. Different browsers can use different logic for
|
||||
* computing it.
|
||||
*/
|
||||
String suggestedFilename();
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,25 +16,18 @@
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import com.microsoft.playwright.options.*;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* {@code FileChooser} objects are dispatched by the page in the [{@code event: Page.filechooser}] event.
|
||||
* {@code FileChooser} objects are dispatched by the page in the {@link Page#onFileChooser Page.onFileChooser()} event.
|
||||
* <pre>{@code
|
||||
* FileChooser fileChooser = page.waitForFileChooser(() -> page.click("upload"));
|
||||
* fileChooser.setFiles(Paths.get("myfile.pdf"));
|
||||
* }</pre>
|
||||
*/
|
||||
public interface FileChooser {
|
||||
class FilePayload {
|
||||
public final String name;
|
||||
public final String mimeType;
|
||||
public final byte[] buffer;
|
||||
|
||||
public FilePayload(String name, String mimeType, byte[] buffer) {
|
||||
this.name = name;
|
||||
this.mimeType = mimeType;
|
||||
this.buffer = buffer;
|
||||
}
|
||||
}
|
||||
|
||||
class SetFilesOptions {
|
||||
/**
|
||||
* Actions that initiate navigations are waiting for these navigations to happen and for pages to start loading. You can
|
||||
@@ -44,15 +37,16 @@ public interface FileChooser {
|
||||
public Boolean noWaitAfter;
|
||||
/**
|
||||
* Maximum time in milliseconds, defaults to 30 seconds, pass {@code 0} to disable timeout. The default value can be changed by
|
||||
* using the [{@code method: BrowserContext.setDefaultTimeout}] or [{@code method: Page.setDefaultTimeout}] methods.
|
||||
* using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()} or {@link Page#setDefaultTimeout
|
||||
* Page.setDefaultTimeout()} methods.
|
||||
*/
|
||||
public Double timeout;
|
||||
|
||||
public SetFilesOptions withNoWaitAfter(boolean noWaitAfter) {
|
||||
public SetFilesOptions setNoWaitAfter(boolean noWaitAfter) {
|
||||
this.noWaitAfter = noWaitAfter;
|
||||
return this;
|
||||
}
|
||||
public SetFilesOptions withTimeout(double timeout) {
|
||||
public SetFilesOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
@@ -69,17 +63,53 @@ public interface FileChooser {
|
||||
* Returns page this file chooser belongs to.
|
||||
*/
|
||||
Page page();
|
||||
default void setFiles(Path file) { setFiles(file, null); }
|
||||
default void setFiles(Path file, SetFilesOptions options) { setFiles(new Path[]{ file }, options); }
|
||||
default void setFiles(Path[] files) { setFiles(files, null); }
|
||||
void setFiles(Path[] files, SetFilesOptions options);
|
||||
default void setFiles(FileChooser.FilePayload file) { setFiles(file, null); }
|
||||
default void setFiles(FileChooser.FilePayload file, SetFilesOptions options) { setFiles(new FileChooser.FilePayload[]{ file }, options); }
|
||||
default void setFiles(FileChooser.FilePayload[] files) { setFiles(files, null); }
|
||||
/**
|
||||
* Sets the value of the file input this chooser is associated with. If some of the {@code filePaths} are relative paths, then
|
||||
* they are resolved relative to the the current working directory. For empty array, clears the selected files.
|
||||
*/
|
||||
void setFiles(FileChooser.FilePayload[] files, SetFilesOptions options);
|
||||
default void setFiles(Path files) {
|
||||
setFiles(files, null);
|
||||
}
|
||||
/**
|
||||
* Sets the value of the file input this chooser is associated with. If some of the {@code filePaths} are relative paths, then
|
||||
* they are resolved relative to the the current working directory. For empty array, clears the selected files.
|
||||
*/
|
||||
void setFiles(Path files, SetFilesOptions options);
|
||||
/**
|
||||
* Sets the value of the file input this chooser is associated with. If some of the {@code filePaths} are relative paths, then
|
||||
* they are resolved relative to the the current working directory. For empty array, clears the selected files.
|
||||
*/
|
||||
default void setFiles(Path[] files) {
|
||||
setFiles(files, null);
|
||||
}
|
||||
/**
|
||||
* Sets the value of the file input this chooser is associated with. If some of the {@code filePaths} are relative paths, then
|
||||
* they are resolved relative to the the current working directory. For empty array, clears the selected files.
|
||||
*/
|
||||
void setFiles(Path[] files, SetFilesOptions options);
|
||||
/**
|
||||
* Sets the value of the file input this chooser is associated with. If some of the {@code filePaths} are relative paths, then
|
||||
* they are resolved relative to the the current working directory. For empty array, clears the selected files.
|
||||
*/
|
||||
default void setFiles(FilePayload files) {
|
||||
setFiles(files, null);
|
||||
}
|
||||
/**
|
||||
* Sets the value of the file input this chooser is associated with. If some of the {@code filePaths} are relative paths, then
|
||||
* they are resolved relative to the the current working directory. For empty array, clears the selected files.
|
||||
*/
|
||||
void setFiles(FilePayload files, SetFilesOptions options);
|
||||
/**
|
||||
* Sets the value of the file input this chooser is associated with. If some of the {@code filePaths} are relative paths, then
|
||||
* they are resolved relative to the the current working directory. For empty array, clears the selected files.
|
||||
*/
|
||||
default void setFiles(FilePayload[] files) {
|
||||
setFiles(files, null);
|
||||
}
|
||||
/**
|
||||
* Sets the value of the file input this chooser is associated with. If some of the {@code filePaths} are relative paths, then
|
||||
* they are resolved relative to the the current working directory. For empty array, clears the selected files.
|
||||
*/
|
||||
void setFiles(FilePayload[] files, SetFilesOptions options);
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,15 +19,19 @@ package com.microsoft.playwright;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* JSHandle represents an in-page JavaScript object. JSHandles can be created with the [{@code method: Page.evaluateHandle}]
|
||||
* method.
|
||||
* JSHandle represents an in-page JavaScript object. JSHandles can be created with the {@link Page#evaluateHandle
|
||||
* Page.evaluateHandle()} method.
|
||||
* <pre>{@code
|
||||
* JSHandle windowHandle = page.evaluateHandle("() => window");
|
||||
* // ...
|
||||
* }</pre>
|
||||
*
|
||||
* <p> JSHandle prevents the referenced JavaScript object being garbage collected unless the handle is exposed with
|
||||
* [{@code method: JSHandle.dispose}]. JSHandles are auto-disposed when their origin frame gets navigated or the parent context
|
||||
* gets destroyed.
|
||||
* <p> JSHandle prevents the referenced JavaScript object being garbage collected unless the handle is exposed with {@link
|
||||
* JSHandle#dispose JSHandle.dispose()}. JSHandles are auto-disposed when their origin frame gets navigated or the parent
|
||||
* context gets destroyed.
|
||||
*
|
||||
* <p> JSHandle instances can be used as an argument in [{@code method: Page.$eval}], [{@code method: Page.evaluate}] and
|
||||
* [{@code method: Page.evaluateHandle}] methods.
|
||||
* <p> JSHandle instances can be used as an argument in {@link Page#evalOnSelector Page.evalOnSelector()}, {@link Page#evaluate
|
||||
* Page.evaluate()} and {@link Page#evaluateHandle Page.evaluateHandle()} methods.
|
||||
*/
|
||||
public interface JSHandle {
|
||||
/**
|
||||
@@ -38,44 +42,95 @@ public interface JSHandle {
|
||||
* The {@code jsHandle.dispose} method stops referencing the element handle.
|
||||
*/
|
||||
void dispose();
|
||||
default Object evaluate(String pageFunction) {
|
||||
return evaluate(pageFunction, null);
|
||||
}
|
||||
/**
|
||||
* Returns the return value of {@code pageFunction}
|
||||
* Returns the return value of {@code expression}.
|
||||
*
|
||||
* <p> This method passes this handle as the first argument to {@code pageFunction}.
|
||||
* <p> This method passes this handle as the first argument to {@code expression}.
|
||||
*
|
||||
* <p> If {@code pageFunction} returns a [Promise], then {@code handle.evaluate} would wait for the promise to resolve and return its
|
||||
* value.
|
||||
* <p> If {@code expression} returns a <a
|
||||
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, then
|
||||
* {@code handle.evaluate} would wait for the promise to resolve and return its value.
|
||||
*
|
||||
* <p> Examples:
|
||||
* <pre>{@code
|
||||
* ElementHandle tweetHandle = page.querySelector(".tweet .retweets");
|
||||
* assertEquals("10 retweets", tweetHandle.evaluate("node => node.innerText"));
|
||||
* }</pre>
|
||||
*
|
||||
* @param pageFunction Function to be evaluated in browser context
|
||||
* @param arg Optional argument to pass to {@code pageFunction}
|
||||
* @param expression JavaScript expression to be evaluated in the browser context. If it looks like a function declaration, it is interpreted
|
||||
* as a function. Otherwise, evaluated as an expression.
|
||||
*/
|
||||
Object evaluate(String pageFunction, Object arg);
|
||||
default JSHandle evaluateHandle(String pageFunction) {
|
||||
return evaluateHandle(pageFunction, null);
|
||||
default Object evaluate(String expression) {
|
||||
return evaluate(expression, null);
|
||||
}
|
||||
/**
|
||||
* Returns the return value of {@code pageFunction} as in-page object (JSHandle).
|
||||
* Returns the return value of {@code expression}.
|
||||
*
|
||||
* <p> This method passes this handle as the first argument to {@code pageFunction}.
|
||||
* <p> This method passes this handle as the first argument to {@code expression}.
|
||||
*
|
||||
* <p> If {@code expression} returns a <a
|
||||
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, then
|
||||
* {@code handle.evaluate} would wait for the promise to resolve and return its value.
|
||||
*
|
||||
* <p> Examples:
|
||||
* <pre>{@code
|
||||
* ElementHandle tweetHandle = page.querySelector(".tweet .retweets");
|
||||
* assertEquals("10 retweets", tweetHandle.evaluate("node => node.innerText"));
|
||||
* }</pre>
|
||||
*
|
||||
* @param expression JavaScript expression to be evaluated in the browser context. If it looks like a function declaration, it is interpreted
|
||||
* as a function. Otherwise, evaluated as an expression.
|
||||
* @param arg Optional argument to pass to {@code expression}.
|
||||
*/
|
||||
Object evaluate(String expression, Object arg);
|
||||
/**
|
||||
* Returns the return value of {@code expression} as a {@code JSHandle}.
|
||||
*
|
||||
* <p> This method passes this handle as the first argument to {@code expression}.
|
||||
*
|
||||
* <p> The only difference between {@code jsHandle.evaluate} and {@code jsHandle.evaluateHandle} is that {@code jsHandle.evaluateHandle} returns
|
||||
* in-page object (JSHandle).
|
||||
* {@code JSHandle}.
|
||||
*
|
||||
* <p> If the function passed to the {@code jsHandle.evaluateHandle} returns a [Promise], then {@code jsHandle.evaluateHandle} would wait
|
||||
* for the promise to resolve and return its value.
|
||||
* <p> If the function passed to the {@code jsHandle.evaluateHandle} returns a <a
|
||||
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, then
|
||||
* {@code jsHandle.evaluateHandle} would wait for the promise to resolve and return its value.
|
||||
*
|
||||
* <p> See [{@code method: Page.evaluateHandle}] for more details.
|
||||
* <p> See {@link Page#evaluateHandle Page.evaluateHandle()} for more details.
|
||||
*
|
||||
* @param pageFunction Function to be evaluated
|
||||
* @param arg Optional argument to pass to {@code pageFunction}
|
||||
* @param expression JavaScript expression to be evaluated in the browser context. If it looks like a function declaration, it is interpreted
|
||||
* as a function. Otherwise, evaluated as an expression.
|
||||
*/
|
||||
JSHandle evaluateHandle(String pageFunction, Object arg);
|
||||
default JSHandle evaluateHandle(String expression) {
|
||||
return evaluateHandle(expression, null);
|
||||
}
|
||||
/**
|
||||
* Returns the return value of {@code expression} as a {@code JSHandle}.
|
||||
*
|
||||
* <p> This method passes this handle as the first argument to {@code expression}.
|
||||
*
|
||||
* <p> The only difference between {@code jsHandle.evaluate} and {@code jsHandle.evaluateHandle} is that {@code jsHandle.evaluateHandle} returns
|
||||
* {@code JSHandle}.
|
||||
*
|
||||
* <p> If the function passed to the {@code jsHandle.evaluateHandle} returns a <a
|
||||
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, then
|
||||
* {@code jsHandle.evaluateHandle} would wait for the promise to resolve and return its value.
|
||||
*
|
||||
* <p> See {@link Page#evaluateHandle Page.evaluateHandle()} for more details.
|
||||
*
|
||||
* @param expression JavaScript expression to be evaluated in the browser context. If it looks like a function declaration, it is interpreted
|
||||
* as a function. Otherwise, evaluated as an expression.
|
||||
* @param arg Optional argument to pass to {@code expression}.
|
||||
*/
|
||||
JSHandle evaluateHandle(String expression, Object arg);
|
||||
/**
|
||||
* The method returns a map with **own property names** as keys and JSHandle instances for the property values.
|
||||
* <pre>{@code
|
||||
* JSHandle handle = page.evaluateHandle("() => ({window, document}"););
|
||||
* Map<String, JSHandle> properties = handle.getProperties();
|
||||
* JSHandle windowHandle = properties.get("window");
|
||||
* JSHandle documentHandle = properties.get("document");
|
||||
* handle.dispose();
|
||||
* }</pre>
|
||||
*/
|
||||
Map<String, JSHandle> getProperties();
|
||||
/**
|
||||
@@ -87,8 +142,8 @@ public interface JSHandle {
|
||||
/**
|
||||
* Returns a JSON representation of the object. If the object has a {@code toJSON} function, it **will not be called**.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> The method will return an empty JSON object if the referenced object is not stringifiable. It will throw an
|
||||
* error if the object has circular references.
|
||||
* <p> <strong>NOTE:</strong> The method will return an empty JSON object if the referenced object is not stringifiable. It will throw an error if the
|
||||
* object has circular references.
|
||||
*/
|
||||
Object jsonValue();
|
||||
}
|
||||
|
||||
@@ -16,27 +16,51 @@
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import com.microsoft.playwright.options.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Keyboard provides an api for managing a virtual keyboard. The high level api is [{@code method: Keyboard.type}], which takes
|
||||
* raw characters and generates proper keydown, keypress/input, and keyup events on your page.
|
||||
* Keyboard provides an api for managing a virtual keyboard. The high level api is {@link Keyboard#type Keyboard.type()},
|
||||
* which takes raw characters and generates proper keydown, keypress/input, and keyup events on your page.
|
||||
*
|
||||
* <p> For finer control, you can use [{@code method: Keyboard.down}], [{@code method: Keyboard.up}], and [{@code method: Keyboard.insertText}]
|
||||
* to manually fire events as if they were generated from a real keyboard.
|
||||
* <p> For finer control, you can use {@link Keyboard#down Keyboard.down()}, {@link Keyboard#up Keyboard.up()}, and {@link
|
||||
* Keyboard#insertText Keyboard.insertText()} to manually fire events as if they were generated from a real keyboard.
|
||||
*
|
||||
* <p> An example of holding down {@code Shift} in order to select and delete some text:
|
||||
* <pre>{@code
|
||||
* page.keyboard().type("Hello World!");
|
||||
* page.keyboard().press("ArrowLeft");
|
||||
* page.keyboard().down("Shift");
|
||||
* for (int i = 0; i < " World".length(); i++)
|
||||
* page.keyboard().press("ArrowLeft");
|
||||
* page.keyboard().up("Shift");
|
||||
* page.keyboard().press("Backspace");
|
||||
* // Result text will end up saying "Hello!"
|
||||
* }</pre>
|
||||
*
|
||||
* <p> An example of pressing uppercase {@code A}
|
||||
* <pre>{@code
|
||||
* page.keyboard().press("Shift+KeyA");
|
||||
* // or
|
||||
* page.keyboard().press("Shift+A");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> An example to trigger select-all with the keyboard
|
||||
* <pre>{@code
|
||||
* // on Windows and Linux
|
||||
* page.keyboard().press("Control+A");
|
||||
* // on macOS
|
||||
* page.keyboard().press("Meta+A");
|
||||
* }</pre>
|
||||
*/
|
||||
public interface Keyboard {
|
||||
enum Modifier { ALT, CONTROL, META, SHIFT }
|
||||
|
||||
class PressOptions {
|
||||
/**
|
||||
* Time to wait between {@code keydown} and {@code keyup} in milliseconds. Defaults to 0.
|
||||
*/
|
||||
public Double delay;
|
||||
|
||||
public PressOptions withDelay(double delay) {
|
||||
public PressOptions setDelay(double delay) {
|
||||
this.delay = delay;
|
||||
return this;
|
||||
}
|
||||
@@ -47,7 +71,7 @@ public interface Keyboard {
|
||||
*/
|
||||
public Double delay;
|
||||
|
||||
public TypeOptions withDelay(double delay) {
|
||||
public TypeOptions setDelay(double delay) {
|
||||
this.delay = delay;
|
||||
return this;
|
||||
}
|
||||
@@ -55,9 +79,10 @@ public interface Keyboard {
|
||||
/**
|
||||
* Dispatches a {@code keydown} event.
|
||||
*
|
||||
* <p> {@code key} can specify the intended [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key)
|
||||
* value or a single character to generate the text for. A superset of the {@code key} values can be found
|
||||
* [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are:
|
||||
* <p> {@code key} can specify the intended <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key">keyboardEvent.key</a> value or a single
|
||||
* character to generate the text for. A superset of the {@code key} values can be found <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values">here</a>. Examples of the keys are:
|
||||
*
|
||||
* <p> {@code F1} - {@code F12}, {@code Digit0}- {@code Digit9}, {@code KeyA}- {@code KeyZ}, {@code Backquote}, {@code Minus}, {@code Equal}, {@code Backslash}, {@code Backspace}, {@code Tab},
|
||||
* {@code Delete}, {@code Escape}, {@code ArrowDown}, {@code End}, {@code Enter}, {@code Home}, {@code Insert}, {@code PageDown}, {@code PageUp}, {@code ArrowRight}, {@code ArrowUp}, etc.
|
||||
@@ -70,11 +95,11 @@ public interface Keyboard {
|
||||
* texts.
|
||||
*
|
||||
* <p> If {@code key} is a modifier key, {@code Shift}, {@code Meta}, {@code Control}, or {@code Alt}, subsequent key presses will be sent with that modifier
|
||||
* active. To release the modifier key, use [{@code method: Keyboard.up}].
|
||||
* active. To release the modifier key, use {@link Keyboard#up Keyboard.up()}.
|
||||
*
|
||||
* <p> After the key is pressed once, subsequent calls to [{@code method: Keyboard.down}] will have
|
||||
* [repeat](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/repeat) set to true. To release the key, use
|
||||
* [{@code method: Keyboard.up}].
|
||||
* <p> After the key is pressed once, subsequent calls to {@link Keyboard#down Keyboard.down()} will have <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/repeat">repeat</a> set to true. To release the key,
|
||||
* use {@link Keyboard#up Keyboard.up()}.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Modifier keys DO influence {@code keyboard.down}. Holding down {@code Shift} will type the text in upper case.
|
||||
*
|
||||
@@ -83,19 +108,20 @@ public interface Keyboard {
|
||||
void down(String key);
|
||||
/**
|
||||
* Dispatches only {@code input} event, does not emit the {@code keydown}, {@code keyup} or {@code keypress} events.
|
||||
* <pre>{@code
|
||||
* page.keyboard().insertText("嗨");
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Modifier keys DO NOT effect {@code keyboard.insertText}. Holding down {@code Shift} will not type the text in upper case.
|
||||
*
|
||||
* @param text Sets input to the specified text value.
|
||||
*/
|
||||
void insertText(String text);
|
||||
default void press(String key) {
|
||||
press(key, null);
|
||||
}
|
||||
/**
|
||||
* {@code key} can specify the intended [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key)
|
||||
* value or a single character to generate the text for. A superset of the {@code key} values can be found
|
||||
* [here](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values). Examples of the keys are:
|
||||
* {@code key} can specify the intended <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key">keyboardEvent.key</a> value or a single
|
||||
* character to generate the text for. A superset of the {@code key} values can be found <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values">here</a>. Examples of the keys are:
|
||||
*
|
||||
* <p> {@code F1} - {@code F12}, {@code Digit0}- {@code Digit9}, {@code KeyA}- {@code KeyZ}, {@code Backquote}, {@code Minus}, {@code Equal}, {@code Backslash}, {@code Backspace}, {@code Tab},
|
||||
* {@code Delete}, {@code Escape}, {@code ArrowDown}, {@code End}, {@code Enter}, {@code Home}, {@code Insert}, {@code PageDown}, {@code PageUp}, {@code ArrowRight}, {@code ArrowUp}, etc.
|
||||
@@ -109,25 +135,94 @@ public interface Keyboard {
|
||||
*
|
||||
* <p> Shortcuts such as {@code key: "Control+o"} or {@code key: "Control+Shift+T"} are supported as well. When speficied with the
|
||||
* modifier, modifier is pressed and being held while the subsequent key is being pressed.
|
||||
* <pre>{@code
|
||||
* Page page = browser.newPage();
|
||||
* page.navigate("https://keycode.info");
|
||||
* page.keyboard().press("A");
|
||||
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("A.png"));
|
||||
* page.keyboard().press("ArrowLeft");
|
||||
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("ArrowLeft.png")));
|
||||
* page.keyboard().press("Shift+O");
|
||||
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("O.png")));
|
||||
* browser.close();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> Shortcut for [{@code method: Keyboard.down}] and [{@code method: Keyboard.up}].
|
||||
* <p> Shortcut for {@link Keyboard#down Keyboard.down()} and {@link Keyboard#up Keyboard.up()}.
|
||||
*
|
||||
* @param key Name of the key to press or a character to generate, such as {@code ArrowLeft} or {@code a}.
|
||||
*/
|
||||
void press(String key, PressOptions delay);
|
||||
default void press(String key) {
|
||||
press(key, null);
|
||||
}
|
||||
/**
|
||||
* {@code key} can specify the intended <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key">keyboardEvent.key</a> value or a single
|
||||
* character to generate the text for. A superset of the {@code key} values can be found <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values">here</a>. Examples of the keys are:
|
||||
*
|
||||
* <p> {@code F1} - {@code F12}, {@code Digit0}- {@code Digit9}, {@code KeyA}- {@code KeyZ}, {@code Backquote}, {@code Minus}, {@code Equal}, {@code Backslash}, {@code Backspace}, {@code Tab},
|
||||
* {@code Delete}, {@code Escape}, {@code ArrowDown}, {@code End}, {@code Enter}, {@code Home}, {@code Insert}, {@code PageDown}, {@code PageUp}, {@code ArrowRight}, {@code ArrowUp}, etc.
|
||||
*
|
||||
* <p> Following modification shortcuts are also supported: {@code Shift}, {@code Control}, {@code Alt}, {@code Meta}, {@code ShiftLeft}.
|
||||
*
|
||||
* <p> Holding down {@code Shift} will type the text that corresponds to the {@code key} in the upper case.
|
||||
*
|
||||
* <p> If {@code key} is a single character, it is case-sensitive, so the values {@code a} and {@code A} will generate different respective
|
||||
* texts.
|
||||
*
|
||||
* <p> Shortcuts such as {@code key: "Control+o"} or {@code key: "Control+Shift+T"} are supported as well. When speficied with the
|
||||
* modifier, modifier is pressed and being held while the subsequent key is being pressed.
|
||||
* <pre>{@code
|
||||
* Page page = browser.newPage();
|
||||
* page.navigate("https://keycode.info");
|
||||
* page.keyboard().press("A");
|
||||
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("A.png"));
|
||||
* page.keyboard().press("ArrowLeft");
|
||||
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("ArrowLeft.png")));
|
||||
* page.keyboard().press("Shift+O");
|
||||
* page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("O.png")));
|
||||
* browser.close();
|
||||
* }</pre>
|
||||
*
|
||||
* <p> Shortcut for {@link Keyboard#down Keyboard.down()} and {@link Keyboard#up Keyboard.up()}.
|
||||
*
|
||||
* @param key Name of the key to press or a character to generate, such as {@code ArrowLeft} or {@code a}.
|
||||
*/
|
||||
void press(String key, PressOptions options);
|
||||
/**
|
||||
* Sends a {@code keydown}, {@code keypress}/{@code input}, and {@code keyup} event for each character in the text.
|
||||
*
|
||||
* <p> To press a special key, like {@code Control} or {@code ArrowDown}, use {@link Keyboard#press Keyboard.press()}.
|
||||
* <pre>{@code
|
||||
* // Types instantly
|
||||
* page.keyboard().type("Hello");
|
||||
* // Types slower, like a user
|
||||
* page.keyboard().type("World", new Keyboard.TypeOptions().setDelay(100));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Modifier keys DO NOT effect {@code keyboard.type}. Holding down {@code Shift} will not type the text in upper case.
|
||||
*
|
||||
* @param text A text to type into a focused element.
|
||||
*/
|
||||
default void type(String text) {
|
||||
type(text, null);
|
||||
}
|
||||
/**
|
||||
* Sends a {@code keydown}, {@code keypress}/{@code input}, and {@code keyup} event for each character in the text.
|
||||
*
|
||||
* <p> To press a special key, like {@code Control} or {@code ArrowDown}, use [{@code method: Keyboard.press}].
|
||||
* <p> To press a special key, like {@code Control} or {@code ArrowDown}, use {@link Keyboard#press Keyboard.press()}.
|
||||
* <pre>{@code
|
||||
* // Types instantly
|
||||
* page.keyboard().type("Hello");
|
||||
* // Types slower, like a user
|
||||
* page.keyboard().type("World", new Keyboard.TypeOptions().setDelay(100));
|
||||
* }</pre>
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> Modifier keys DO NOT effect {@code keyboard.type}. Holding down {@code Shift} will not type the text in upper case.
|
||||
*
|
||||
* @param text A text to type into a focused element.
|
||||
*/
|
||||
void type(String text, TypeOptions delay);
|
||||
void type(String text, TypeOptions options);
|
||||
/**
|
||||
* Dispatches a {@code keyup} event.
|
||||
*
|
||||
|
||||
@@ -16,21 +16,30 @@
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import com.microsoft.playwright.options.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* The Mouse class operates in main-frame CSS pixels relative to the top-left corner of the viewport.
|
||||
*
|
||||
* <p> Every {@code page} object has its own Mouse, accessible with [{@code property: Page.mouse}].
|
||||
* <p> Every {@code page} object has its own Mouse, accessible with {@link Page#mouse Page.mouse()}.
|
||||
* <pre>{@code
|
||||
* // Using ‘page.mouse’ to trace a 100x100 square.
|
||||
* page.mouse().move(0, 0);
|
||||
* page.mouse().down();
|
||||
* page.mouse().move(0, 100);
|
||||
* page.mouse().move(100, 100);
|
||||
* page.mouse().move(100, 0);
|
||||
* page.mouse().move(0, 0);
|
||||
* page.mouse().up();
|
||||
* }</pre>
|
||||
*/
|
||||
public interface Mouse {
|
||||
enum Button { LEFT, MIDDLE, RIGHT }
|
||||
|
||||
class ClickOptions {
|
||||
/**
|
||||
* Defaults to {@code left}.
|
||||
*/
|
||||
public Button button;
|
||||
public MouseButton button;
|
||||
/**
|
||||
* defaults to 1. See [UIEvent.detail].
|
||||
*/
|
||||
@@ -40,15 +49,15 @@ public interface Mouse {
|
||||
*/
|
||||
public Double delay;
|
||||
|
||||
public ClickOptions withButton(Button button) {
|
||||
public ClickOptions setButton(MouseButton button) {
|
||||
this.button = button;
|
||||
return this;
|
||||
}
|
||||
public ClickOptions withClickCount(int clickCount) {
|
||||
public ClickOptions setClickCount(int clickCount) {
|
||||
this.clickCount = clickCount;
|
||||
return this;
|
||||
}
|
||||
public ClickOptions withDelay(double delay) {
|
||||
public ClickOptions setDelay(double delay) {
|
||||
this.delay = delay;
|
||||
return this;
|
||||
}
|
||||
@@ -57,17 +66,17 @@ public interface Mouse {
|
||||
/**
|
||||
* Defaults to {@code left}.
|
||||
*/
|
||||
public Button button;
|
||||
public MouseButton button;
|
||||
/**
|
||||
* Time to wait between {@code mousedown} and {@code mouseup} in milliseconds. Defaults to 0.
|
||||
*/
|
||||
public Double delay;
|
||||
|
||||
public DblclickOptions withButton(Button button) {
|
||||
public DblclickOptions setButton(MouseButton button) {
|
||||
this.button = button;
|
||||
return this;
|
||||
}
|
||||
public DblclickOptions withDelay(double delay) {
|
||||
public DblclickOptions setDelay(double delay) {
|
||||
this.delay = delay;
|
||||
return this;
|
||||
}
|
||||
@@ -76,17 +85,17 @@ public interface Mouse {
|
||||
/**
|
||||
* Defaults to {@code left}.
|
||||
*/
|
||||
public Button button;
|
||||
public MouseButton button;
|
||||
/**
|
||||
* defaults to 1. See [UIEvent.detail].
|
||||
*/
|
||||
public Integer clickCount;
|
||||
|
||||
public DownOptions withButton(Button button) {
|
||||
public DownOptions setButton(MouseButton button) {
|
||||
this.button = button;
|
||||
return this;
|
||||
}
|
||||
public DownOptions withClickCount(int clickCount) {
|
||||
public DownOptions setClickCount(int clickCount) {
|
||||
this.clickCount = clickCount;
|
||||
return this;
|
||||
}
|
||||
@@ -97,7 +106,7 @@ public interface Mouse {
|
||||
*/
|
||||
public Integer steps;
|
||||
|
||||
public MoveOptions withSteps(int steps) {
|
||||
public MoveOptions setSteps(int steps) {
|
||||
this.steps = steps;
|
||||
return this;
|
||||
}
|
||||
@@ -106,36 +115,46 @@ public interface Mouse {
|
||||
/**
|
||||
* Defaults to {@code left}.
|
||||
*/
|
||||
public Button button;
|
||||
public MouseButton button;
|
||||
/**
|
||||
* defaults to 1. See [UIEvent.detail].
|
||||
*/
|
||||
public Integer clickCount;
|
||||
|
||||
public UpOptions withButton(Button button) {
|
||||
public UpOptions setButton(MouseButton button) {
|
||||
this.button = button;
|
||||
return this;
|
||||
}
|
||||
public UpOptions withClickCount(int clickCount) {
|
||||
public UpOptions setClickCount(int clickCount) {
|
||||
this.clickCount = clickCount;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Shortcut for {@link Mouse#move Mouse.move()}, {@link Mouse#down Mouse.down()}, {@link Mouse#up Mouse.up()}.
|
||||
*/
|
||||
default void click(double x, double y) {
|
||||
click(x, y, null);
|
||||
}
|
||||
/**
|
||||
* Shortcut for [{@code method: Mouse.move}], [{@code method: Mouse.down}], [{@code method: Mouse.up}].
|
||||
* Shortcut for {@link Mouse#move Mouse.move()}, {@link Mouse#down Mouse.down()}, {@link Mouse#up Mouse.up()}.
|
||||
*/
|
||||
void click(double x, double y, ClickOptions options);
|
||||
/**
|
||||
* Shortcut for {@link Mouse#move Mouse.move()}, {@link Mouse#down Mouse.down()}, {@link Mouse#up Mouse.up()}, {@link
|
||||
* Mouse#down Mouse.down()} and {@link Mouse#up Mouse.up()}.
|
||||
*/
|
||||
default void dblclick(double x, double y) {
|
||||
dblclick(x, y, null);
|
||||
}
|
||||
/**
|
||||
* Shortcut for [{@code method: Mouse.move}], [{@code method: Mouse.down}], [{@code method: Mouse.up}], [{@code method: Mouse.down}] and
|
||||
* [{@code method: Mouse.up}].
|
||||
* Shortcut for {@link Mouse#move Mouse.move()}, {@link Mouse#down Mouse.down()}, {@link Mouse#up Mouse.up()}, {@link
|
||||
* Mouse#down Mouse.down()} and {@link Mouse#up Mouse.up()}.
|
||||
*/
|
||||
void dblclick(double x, double y, DblclickOptions options);
|
||||
/**
|
||||
* Dispatches a {@code mousedown} event.
|
||||
*/
|
||||
default void down() {
|
||||
down(null);
|
||||
}
|
||||
@@ -143,6 +162,9 @@ public interface Mouse {
|
||||
* Dispatches a {@code mousedown} event.
|
||||
*/
|
||||
void down(DownOptions options);
|
||||
/**
|
||||
* Dispatches a {@code mousemove} event.
|
||||
*/
|
||||
default void move(double x, double y) {
|
||||
move(x, y, null);
|
||||
}
|
||||
@@ -150,6 +172,9 @@ public interface Mouse {
|
||||
* Dispatches a {@code mousemove} event.
|
||||
*/
|
||||
void move(double x, double y, MoveOptions options);
|
||||
/**
|
||||
* Dispatches a {@code mouseup} event.
|
||||
*/
|
||||
default void up() {
|
||||
up(null);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,34 +22,58 @@ import java.util.*;
|
||||
/**
|
||||
* Playwright module provides a method to launch a browser instance. The following is a typical example of using Playwright
|
||||
* to drive automation:
|
||||
* <pre>{@code
|
||||
* import com.microsoft.playwright.*;
|
||||
*
|
||||
* public class Example {
|
||||
* public static void main(String[] args) {
|
||||
* try (Playwright playwright = Playwright.create()) {
|
||||
* BrowserType chromium = playwright.chromium();
|
||||
* Browser browser = chromium.launch();
|
||||
* Page page = browser.newPage();
|
||||
* page.navigate("http://example.com");
|
||||
* // other actions...
|
||||
* browser.close();
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*/
|
||||
public interface Playwright {
|
||||
public interface Playwright extends AutoCloseable {
|
||||
/**
|
||||
* This object can be used to launch or connect to Chromium, returning instances of {@code ChromiumBrowser}.
|
||||
*/
|
||||
BrowserType chromium();
|
||||
/**
|
||||
* Returns a dictionary of devices to be used with [{@code method: Browser.newContext}] or [{@code method: Browser.newPage}].
|
||||
*/
|
||||
Map<String, DeviceDescriptor> devices();
|
||||
/**
|
||||
* This object can be used to launch or connect to Firefox, returning instances of {@code FirefoxBrowser}.
|
||||
*/
|
||||
BrowserType firefox();
|
||||
/**
|
||||
* Selectors can be used to install custom selector engines. See
|
||||
* [Working with selectors](./selectors.md#working-with-selectors) for more information.
|
||||
* Selectors can be used to install custom selector engines. See <a
|
||||
* href="https://playwright.dev/java/docs/selectors/">Working with selectors</a> for more information.
|
||||
*/
|
||||
Selectors selectors();
|
||||
/**
|
||||
* This object can be used to launch or connect to WebKit, returning instances of {@code WebKitBrowser}.
|
||||
*/
|
||||
BrowserType webkit();
|
||||
|
||||
/**
|
||||
* Terminates this instance of Playwright, will also close all created browsers if they are still running.
|
||||
*/
|
||||
void close();
|
||||
/**
|
||||
* Launches new Playwright driver process and connects to it. {@link Playwright#close Playwright.close()} should be called
|
||||
* when the instance is no longer needed.
|
||||
* <pre>{@code
|
||||
* Playwright playwright = Playwright.create()) {
|
||||
* Browser browser = playwright.webkit().launch();
|
||||
* Page page = browser.newPage();
|
||||
* page.navigate("https://www.w3.org/");
|
||||
* playwright.close();
|
||||
* }</pre>
|
||||
*/
|
||||
static Playwright create() {
|
||||
return PlaywrightImpl.create();
|
||||
}
|
||||
|
||||
void close() throws Exception;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,117 +16,39 @@
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import com.microsoft.playwright.options.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Whenever the page sends a request for a network resource the following sequence of events are emitted by {@code Page}:
|
||||
* - [{@code event: Page.request}] emitted when the request is issued by the page.
|
||||
* - [{@code event: Page.response}] emitted when/if the response status and headers are received for the request.
|
||||
* - [{@code event: Page.requestfinished}] emitted when the response body is downloaded and the request is complete.
|
||||
* <ul>
|
||||
* <li> {@link Page#onRequest Page.onRequest()} emitted when the request is issued by the page.</li>
|
||||
* <li> {@link Page#onResponse Page.onResponse()} emitted when/if the response status and headers are received for the request.</li>
|
||||
* <li> {@link Page#onRequestFinished Page.onRequestFinished()} emitted when the response body is downloaded and the request is
|
||||
* complete.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p> If request fails at some point, then instead of {@code 'requestfinished'} event (and possibly instead of 'response' event),
|
||||
* the [{@code event: Page.requestfailed}] event is emitted.
|
||||
* <p> If request fails at some point, then instead of {@code "requestfinished"} event (and possibly instead of 'response' event),
|
||||
* the {@link Page#onRequestFailed Page.onRequestFailed()} event is emitted.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will
|
||||
* complete with {@code 'requestfinished'} event.
|
||||
* <p> <strong>NOTE:</strong> HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will complete
|
||||
* with {@code "requestfinished"} event.
|
||||
*
|
||||
* <p> If request gets a 'redirect' response, the request is successfully finished with the 'requestfinished' event, and a new
|
||||
* request is issued to a redirected url.
|
||||
*/
|
||||
public interface Request {
|
||||
class RequestFailure {
|
||||
/**
|
||||
* Human-readable error message, e.g. {@code 'net::ERR_FAILED'}.
|
||||
*/
|
||||
private String errorText;
|
||||
|
||||
public RequestFailure(String errorText) {
|
||||
this.errorText = errorText;
|
||||
}
|
||||
public String errorText() {
|
||||
return this.errorText;
|
||||
}
|
||||
}
|
||||
class RequestTiming {
|
||||
/**
|
||||
* Request start time in milliseconds elapsed since January 1, 1970 00:00:00 UTC
|
||||
*/
|
||||
private double startTime;
|
||||
/**
|
||||
* Time immediately before the browser starts the domain name lookup for the resource. The value is given in milliseconds
|
||||
* relative to {@code startTime}, -1 if not available.
|
||||
*/
|
||||
private double domainLookupStart;
|
||||
/**
|
||||
* Time immediately after the browser starts the domain name lookup for the resource. The value is given in milliseconds
|
||||
* relative to {@code startTime}, -1 if not available.
|
||||
*/
|
||||
private double domainLookupEnd;
|
||||
/**
|
||||
* Time immediately before the user agent starts establishing the connection to the server to retrieve the resource. The
|
||||
* value is given in milliseconds relative to {@code startTime}, -1 if not available.
|
||||
*/
|
||||
private double connectStart;
|
||||
/**
|
||||
* Time immediately before the browser starts the handshake process to secure the current connection. The value is given in
|
||||
* milliseconds relative to {@code startTime}, -1 if not available.
|
||||
*/
|
||||
private double secureConnectionStart;
|
||||
/**
|
||||
* Time immediately before the user agent starts establishing the connection to the server to retrieve the resource. The
|
||||
* value is given in milliseconds relative to {@code startTime}, -1 if not available.
|
||||
*/
|
||||
private double connectEnd;
|
||||
/**
|
||||
* Time immediately before the browser starts requesting the resource from the server, cache, or local resource. The value
|
||||
* is given in milliseconds relative to {@code startTime}, -1 if not available.
|
||||
*/
|
||||
private double requestStart;
|
||||
/**
|
||||
* Time immediately after the browser starts requesting the resource from the server, cache, or local resource. The value
|
||||
* is given in milliseconds relative to {@code startTime}, -1 if not available.
|
||||
*/
|
||||
private double responseStart;
|
||||
/**
|
||||
* Time immediately after the browser receives the last byte of the resource or immediately before the transport connection
|
||||
* is closed, whichever comes first. The value is given in milliseconds relative to {@code startTime}, -1 if not available.
|
||||
*/
|
||||
private double responseEnd;
|
||||
|
||||
public double startTime() {
|
||||
return this.startTime;
|
||||
}
|
||||
public double domainLookupStart() {
|
||||
return this.domainLookupStart;
|
||||
}
|
||||
public double domainLookupEnd() {
|
||||
return this.domainLookupEnd;
|
||||
}
|
||||
public double connectStart() {
|
||||
return this.connectStart;
|
||||
}
|
||||
public double secureConnectionStart() {
|
||||
return this.secureConnectionStart;
|
||||
}
|
||||
public double connectEnd() {
|
||||
return this.connectEnd;
|
||||
}
|
||||
public double requestStart() {
|
||||
return this.requestStart;
|
||||
}
|
||||
public double responseStart() {
|
||||
return this.responseStart;
|
||||
}
|
||||
public double responseEnd() {
|
||||
return this.responseEnd;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The method returns {@code null} unless this request has failed, as reported by {@code requestfailed} event.
|
||||
*
|
||||
* <p> Example of logging of all the failed requests:
|
||||
* <pre>{@code
|
||||
* page.onRequestFailed(request -> {
|
||||
* System.out.println(request.url() + " " + request.failure());
|
||||
* });
|
||||
* }</pre>
|
||||
*/
|
||||
RequestFailure failure();
|
||||
String failure();
|
||||
/**
|
||||
* Returns the {@code Frame} that initiated this request.
|
||||
*/
|
||||
@@ -159,14 +81,25 @@ public interface Request {
|
||||
* construct the whole redirect chain by repeatedly calling {@code redirectedFrom()}.
|
||||
*
|
||||
* <p> For example, if the website {@code http://example.com} redirects to {@code https://example.com}:
|
||||
* <pre>{@code
|
||||
* Response response = page.navigate("http://example.com");
|
||||
* System.out.println(response.request().redirectedFrom().url()); // "http://example.com"
|
||||
* }</pre>
|
||||
*
|
||||
* <p> If the website {@code https://google.com} has no redirects:
|
||||
* <pre>{@code
|
||||
* Response response = page.navigate("https://google.com");
|
||||
* System.out.println(response.request().redirectedFrom()); // null
|
||||
* }</pre>
|
||||
*/
|
||||
Request redirectedFrom();
|
||||
/**
|
||||
* New request issued by the browser if the server responded with redirect.
|
||||
*
|
||||
* <p> This method is the opposite of [{@code method: Request.redirectedFrom}]:
|
||||
* <p> This method is the opposite of {@link Request#redirectedFrom Request.redirectedFrom()}:
|
||||
* <pre>{@code
|
||||
* System.out.println(request.redirectedFrom().redirectedTo() == request); // true
|
||||
* }</pre>
|
||||
*/
|
||||
Request redirectedTo();
|
||||
/**
|
||||
@@ -181,10 +114,17 @@ public interface Request {
|
||||
Response response();
|
||||
/**
|
||||
* Returns resource timing information for given request. Most of the timing values become available upon the response,
|
||||
* {@code responseEnd} becomes available when request finishes. Find more information at
|
||||
* [Resource Timing API](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming).
|
||||
* {@code responseEnd} becomes available when request finishes. Find more information at <a
|
||||
* href="https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming">Resource Timing API</a>.
|
||||
* <pre>{@code
|
||||
* page.onRequestFinished(request -> {
|
||||
* Timing timing = request.timing();
|
||||
* System.out.println(timing.responseEnd - timing.startTime);
|
||||
* });
|
||||
* page.navigate("http://example.com");
|
||||
* }</pre>
|
||||
*/
|
||||
RequestTiming timing();
|
||||
Timing timing();
|
||||
/**
|
||||
* URL of the request.
|
||||
*/
|
||||
|
||||
@@ -21,11 +21,11 @@ import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Whenever a network route is set up with [{@code method: Page.route}] or [{@code method: BrowserContext.route}], the {@code Route} object
|
||||
* allows to handle the route.
|
||||
* Whenever a network route is set up with {@link Page#route Page.route()} or {@link BrowserContext#route
|
||||
* BrowserContext.route()}, the {@code Route} object allows to handle the route.
|
||||
*/
|
||||
public interface Route {
|
||||
class ContinueOptions {
|
||||
class ResumeOptions {
|
||||
/**
|
||||
* If set changes the request HTTP headers. Header values will be converted to a string.
|
||||
*/
|
||||
@@ -37,38 +37,41 @@ public interface Route {
|
||||
/**
|
||||
* If set changes the post data of request
|
||||
*/
|
||||
public byte[] postData;
|
||||
public Object postData;
|
||||
/**
|
||||
* If set changes the request URL. New URL must have same protocol as original one.
|
||||
*/
|
||||
public String url;
|
||||
|
||||
public ContinueOptions withHeaders(Map<String, String> headers) {
|
||||
public ResumeOptions setHeaders(Map<String, String> headers) {
|
||||
this.headers = headers;
|
||||
return this;
|
||||
}
|
||||
public ContinueOptions withMethod(String method) {
|
||||
public ResumeOptions setMethod(String method) {
|
||||
this.method = method;
|
||||
return this;
|
||||
}
|
||||
public ContinueOptions withPostData(String postData) {
|
||||
this.postData = postData.getBytes(StandardCharsets.UTF_8);
|
||||
return this;
|
||||
}
|
||||
public ContinueOptions withPostData(byte[] postData) {
|
||||
public ResumeOptions setPostData(String postData) {
|
||||
this.postData = postData;
|
||||
return this;
|
||||
}
|
||||
public ContinueOptions withUrl(String url) {
|
||||
public ResumeOptions setPostData(byte[] postData) {
|
||||
this.postData = postData;
|
||||
return this;
|
||||
}
|
||||
public ResumeOptions setUrl(String url) {
|
||||
this.url = url;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class FulfillOptions {
|
||||
/**
|
||||
* Response body.
|
||||
* Optional response body as text.
|
||||
*/
|
||||
public String body;
|
||||
/**
|
||||
* Optional response body as raw bytes.
|
||||
*/
|
||||
public byte[] bodyBytes;
|
||||
/**
|
||||
* If set, equals to setting {@code Content-Type} response header.
|
||||
@@ -88,31 +91,34 @@ public interface Route {
|
||||
*/
|
||||
public Integer status;
|
||||
|
||||
public FulfillOptions withBody(byte[] body) {
|
||||
this.bodyBytes = body;
|
||||
return this;
|
||||
}
|
||||
public FulfillOptions withBody(String body) {
|
||||
public FulfillOptions setBody(String body) {
|
||||
this.body = body;
|
||||
return this;
|
||||
}
|
||||
public FulfillOptions withContentType(String contentType) {
|
||||
public FulfillOptions setBodyBytes(byte[] bodyBytes) {
|
||||
this.bodyBytes = bodyBytes;
|
||||
return this;
|
||||
}
|
||||
public FulfillOptions setContentType(String contentType) {
|
||||
this.contentType = contentType;
|
||||
return this;
|
||||
}
|
||||
public FulfillOptions withHeaders(Map<String, String> headers) {
|
||||
public FulfillOptions setHeaders(Map<String, String> headers) {
|
||||
this.headers = headers;
|
||||
return this;
|
||||
}
|
||||
public FulfillOptions withPath(Path path) {
|
||||
public FulfillOptions setPath(Path path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
public FulfillOptions withStatus(int status) {
|
||||
public FulfillOptions setStatus(int status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Aborts the route's request.
|
||||
*/
|
||||
default void abort() {
|
||||
abort(null);
|
||||
}
|
||||
@@ -120,36 +126,94 @@ public interface Route {
|
||||
* Aborts the route's request.
|
||||
*
|
||||
* @param errorCode Optional error code. Defaults to {@code failed}, could be one of the following:
|
||||
* - {@code 'aborted'} - An operation was aborted (due to user action)
|
||||
* - {@code 'accessdenied'} - Permission to access a resource, other than the network, was denied
|
||||
* - {@code 'addressunreachable'} - The IP address is unreachable. This usually means that there is no route to the specified
|
||||
* host or network.
|
||||
* - {@code 'blockedbyclient'} - The client chose to block the request.
|
||||
* - {@code 'blockedbyresponse'} - The request failed because the response was delivered along with requirements which are not
|
||||
* met ('X-Frame-Options' and 'Content-Security-Policy' ancestor checks, for instance).
|
||||
* - {@code 'connectionaborted'} - A connection timed out as a result of not receiving an ACK for data sent.
|
||||
* - {@code 'connectionclosed'} - A connection was closed (corresponding to a TCP FIN).
|
||||
* - {@code 'connectionfailed'} - A connection attempt failed.
|
||||
* - {@code 'connectionrefused'} - A connection attempt was refused.
|
||||
* - {@code 'connectionreset'} - A connection was reset (corresponding to a TCP RST).
|
||||
* - {@code 'internetdisconnected'} - The Internet connection has been lost.
|
||||
* - {@code 'namenotresolved'} - The host name could not be resolved.
|
||||
* - {@code 'timedout'} - An operation timed out.
|
||||
* - {@code 'failed'} - A generic failure occurred.
|
||||
* <ul>
|
||||
* <li> {@code "aborted"} - An operation was aborted (due to user action)</li>
|
||||
* <li> {@code "accessdenied"} - Permission to access a resource, other than the network, was denied</li>
|
||||
* <li> {@code "addressunreachable"} - The IP address is unreachable. This usually means that there is no route to the specified host
|
||||
* or network.</li>
|
||||
* <li> {@code "blockedbyclient"} - The client chose to block the request.</li>
|
||||
* <li> {@code "blockedbyresponse"} - The request failed because the response was delivered along with requirements which are not met
|
||||
* ('X-Frame-Options' and 'Content-Security-Policy' ancestor checks, for instance).</li>
|
||||
* <li> {@code "connectionaborted"} - A connection timed out as a result of not receiving an ACK for data sent.</li>
|
||||
* <li> {@code "connectionclosed"} - A connection was closed (corresponding to a TCP FIN).</li>
|
||||
* <li> {@code "connectionfailed"} - A connection attempt failed.</li>
|
||||
* <li> {@code "connectionrefused"} - A connection attempt was refused.</li>
|
||||
* <li> {@code "connectionreset"} - A connection was reset (corresponding to a TCP RST).</li>
|
||||
* <li> {@code "internetdisconnected"} - The Internet connection has been lost.</li>
|
||||
* <li> {@code "namenotresolved"} - The host name could not be resolved.</li>
|
||||
* <li> {@code "timedout"} - An operation timed out.</li>
|
||||
* <li> {@code "failed"} - A generic failure occurred.</li>
|
||||
* </ul>
|
||||
*/
|
||||
void abort(String errorCode);
|
||||
default void continue_() {
|
||||
continue_(null);
|
||||
/**
|
||||
* Continues route's request with optional overrides.
|
||||
* <pre>{@code
|
||||
* page.route("**\/*", route -> {
|
||||
* // Override headers
|
||||
* Map<String, String> headers = new HashMap<>(route.request().headers());
|
||||
* headers.put("foo", "bar"); // set "foo" header
|
||||
* headers.remove("origin"); // remove "origin" header
|
||||
* route.resume(new Route.ResumeOptions().setHeaders(headers));
|
||||
* });
|
||||
* }</pre>
|
||||
*/
|
||||
default void resume() {
|
||||
resume(null);
|
||||
}
|
||||
/**
|
||||
* Continues route's request with optional overrides.
|
||||
* <pre>{@code
|
||||
* page.route("**\/*", route -> {
|
||||
* // Override headers
|
||||
* Map<String, String> headers = new HashMap<>(route.request().headers());
|
||||
* headers.put("foo", "bar"); // set "foo" header
|
||||
* headers.remove("origin"); // remove "origin" header
|
||||
* route.resume(new Route.ResumeOptions().setHeaders(headers));
|
||||
* });
|
||||
* }</pre>
|
||||
*/
|
||||
void resume(ResumeOptions options);
|
||||
/**
|
||||
* Fulfills route's request with given response.
|
||||
*
|
||||
* <p> An example of fulfilling all requests with 404 responses:
|
||||
* <pre>{@code
|
||||
* page.route("**\/*", route -> {
|
||||
* route.fulfill(new Route.FulfillOptions()
|
||||
* .setStatus(404)
|
||||
* .setContentType("text/plain")
|
||||
* .setBody("Not Found!"));
|
||||
* });
|
||||
* }</pre>
|
||||
*
|
||||
* <p> An example of serving static file:
|
||||
* <pre>{@code
|
||||
* page.route("**\/xhr_endpoint", route -> route.fulfill(
|
||||
* new Route.FulfillOptions().setPath(Paths.get("mock_data.json")));
|
||||
* }</pre>
|
||||
*/
|
||||
void continue_(ContinueOptions options);
|
||||
default void fulfill() {
|
||||
fulfill(null);
|
||||
}
|
||||
/**
|
||||
* Fulfills route's request with given response.
|
||||
*
|
||||
* <p> An example of fulfilling all requests with 404 responses:
|
||||
* <pre>{@code
|
||||
* page.route("**\/*", route -> {
|
||||
* route.fulfill(new Route.FulfillOptions()
|
||||
* .setStatus(404)
|
||||
* .setContentType("text/plain")
|
||||
* .setBody("Not Found!"));
|
||||
* });
|
||||
* }</pre>
|
||||
*
|
||||
* <p> An example of serving static file:
|
||||
* <pre>{@code
|
||||
* page.route("**\/xhr_endpoint", route -> route.fulfill(
|
||||
* new Route.FulfillOptions().setPath(Paths.get("mock_data.json")));
|
||||
* }</pre>
|
||||
*/
|
||||
void fulfill(FulfillOptions options);
|
||||
/**
|
||||
|
||||
@@ -20,8 +20,8 @@ import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Selectors can be used to install custom selector engines. See
|
||||
* [Working with selectors](./selectors.md#working-with-selectors) for more information.
|
||||
* Selectors can be used to install custom selector engines. See <a
|
||||
* href="https://playwright.dev/java/docs/selectors/">Working with selectors</a> for more information.
|
||||
*/
|
||||
public interface Selectors {
|
||||
class RegisterOptions {
|
||||
@@ -32,22 +32,146 @@ public interface Selectors {
|
||||
*/
|
||||
public Boolean contentScript;
|
||||
|
||||
public RegisterOptions withContentScript(boolean contentScript) {
|
||||
public RegisterOptions setContentScript(boolean contentScript) {
|
||||
this.contentScript = contentScript;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
default void register(String name, String script) { register(name, script, null); }
|
||||
void register(String name, String script, RegisterOptions options);
|
||||
default void register(String name, Path path) { register(name, path, null); }
|
||||
/**
|
||||
* An example of registering selector engine that queries elements based on a tag name:
|
||||
*
|
||||
* <pre>{@code
|
||||
* // Script that evaluates to a selector engine instance.
|
||||
* String createTagNameEngine = "{\n" +
|
||||
* " // Returns the first element matching given selector in the root's subtree.\n" +
|
||||
* " query(root, selector) {\n" +
|
||||
* " return root.querySelector(selector);\n" +
|
||||
* " },\n" +
|
||||
* " // Returns all elements matching given selector in the root's subtree.\n" +
|
||||
* " queryAll(root, selector) {\n" +
|
||||
* " return Array.from(root.querySelectorAll(selector));\n" +
|
||||
* " }\n" +
|
||||
* "}";
|
||||
* // Register the engine. Selectors will be prefixed with "tag=".
|
||||
* playwright.selectors().register("tag", createTagNameEngine);
|
||||
* Browser browser = playwright.firefox().launch();
|
||||
* Page page = browser.newPage();
|
||||
* page.setContent("<div><button>Click me</button></div>");
|
||||
* // Use the selector prefixed with its name.
|
||||
* ElementHandle button = page.querySelector("tag=button");
|
||||
* // Combine it with other selector engines.
|
||||
* page.click("tag=div >> text=\"Click me\"");
|
||||
* // Can use it in any methods supporting selectors.
|
||||
* int buttonCount = (int) page.evalOnSelectorAll("tag=button", "buttons => buttons.length");
|
||||
* browser.close();
|
||||
* }</pre>
|
||||
*
|
||||
* @param name Name that is used in selectors as a prefix, e.g. {@code {name: 'foo'}} enables {@code foo=myselectorbody} selectors. May only
|
||||
* contain {@code [a-zA-Z0-9_]} characters.
|
||||
* @param script Script that evaluates to a selector engine instance.
|
||||
*/
|
||||
void register(String name, Path path, RegisterOptions options);
|
||||
default void register(String name, String script) {
|
||||
register(name, script, null);
|
||||
}
|
||||
/**
|
||||
* An example of registering selector engine that queries elements based on a tag name:
|
||||
* <pre>{@code
|
||||
* // Script that evaluates to a selector engine instance.
|
||||
* String createTagNameEngine = "{\n" +
|
||||
* " // Returns the first element matching given selector in the root's subtree.\n" +
|
||||
* " query(root, selector) {\n" +
|
||||
* " return root.querySelector(selector);\n" +
|
||||
* " },\n" +
|
||||
* " // Returns all elements matching given selector in the root's subtree.\n" +
|
||||
* " queryAll(root, selector) {\n" +
|
||||
* " return Array.from(root.querySelectorAll(selector));\n" +
|
||||
* " }\n" +
|
||||
* "}";
|
||||
* // Register the engine. Selectors will be prefixed with "tag=".
|
||||
* playwright.selectors().register("tag", createTagNameEngine);
|
||||
* Browser browser = playwright.firefox().launch();
|
||||
* Page page = browser.newPage();
|
||||
* page.setContent("<div><button>Click me</button></div>");
|
||||
* // Use the selector prefixed with its name.
|
||||
* ElementHandle button = page.querySelector("tag=button");
|
||||
* // Combine it with other selector engines.
|
||||
* page.click("tag=div >> text=\"Click me\"");
|
||||
* // Can use it in any methods supporting selectors.
|
||||
* int buttonCount = (int) page.evalOnSelectorAll("tag=button", "buttons => buttons.length");
|
||||
* browser.close();
|
||||
* }</pre>
|
||||
*
|
||||
* @param name Name that is used in selectors as a prefix, e.g. {@code {name: 'foo'}} enables {@code foo=myselectorbody} selectors. May only
|
||||
* contain {@code [a-zA-Z0-9_]} characters.
|
||||
* @param script Script that evaluates to a selector engine instance.
|
||||
*/
|
||||
void register(String name, String script, RegisterOptions options);
|
||||
/**
|
||||
* An example of registering selector engine that queries elements based on a tag name:
|
||||
* <pre>{@code
|
||||
* // Script that evaluates to a selector engine instance.
|
||||
* String createTagNameEngine = "{\n" +
|
||||
* " // Returns the first element matching given selector in the root's subtree.\n" +
|
||||
* " query(root, selector) {\n" +
|
||||
* " return root.querySelector(selector);\n" +
|
||||
* " },\n" +
|
||||
* " // Returns all elements matching given selector in the root's subtree.\n" +
|
||||
* " queryAll(root, selector) {\n" +
|
||||
* " return Array.from(root.querySelectorAll(selector));\n" +
|
||||
* " }\n" +
|
||||
* "}";
|
||||
* // Register the engine. Selectors will be prefixed with "tag=".
|
||||
* playwright.selectors().register("tag", createTagNameEngine);
|
||||
* Browser browser = playwright.firefox().launch();
|
||||
* Page page = browser.newPage();
|
||||
* page.setContent("<div><button>Click me</button></div>");
|
||||
* // Use the selector prefixed with its name.
|
||||
* ElementHandle button = page.querySelector("tag=button");
|
||||
* // Combine it with other selector engines.
|
||||
* page.click("tag=div >> text=\"Click me\"");
|
||||
* // Can use it in any methods supporting selectors.
|
||||
* int buttonCount = (int) page.evalOnSelectorAll("tag=button", "buttons => buttons.length");
|
||||
* browser.close();
|
||||
* }</pre>
|
||||
*
|
||||
* @param name Name that is used in selectors as a prefix, e.g. {@code {name: 'foo'}} enables {@code foo=myselectorbody} selectors. May only
|
||||
* contain {@code [a-zA-Z0-9_]} characters.
|
||||
* @param script Script that evaluates to a selector engine instance.
|
||||
*/
|
||||
default void register(String name, Path script) {
|
||||
register(name, script, null);
|
||||
}
|
||||
/**
|
||||
* An example of registering selector engine that queries elements based on a tag name:
|
||||
* <pre>{@code
|
||||
* // Script that evaluates to a selector engine instance.
|
||||
* String createTagNameEngine = "{\n" +
|
||||
* " // Returns the first element matching given selector in the root's subtree.\n" +
|
||||
* " query(root, selector) {\n" +
|
||||
* " return root.querySelector(selector);\n" +
|
||||
* " },\n" +
|
||||
* " // Returns all elements matching given selector in the root's subtree.\n" +
|
||||
* " queryAll(root, selector) {\n" +
|
||||
* " return Array.from(root.querySelectorAll(selector));\n" +
|
||||
* " }\n" +
|
||||
* "}";
|
||||
* // Register the engine. Selectors will be prefixed with "tag=".
|
||||
* playwright.selectors().register("tag", createTagNameEngine);
|
||||
* Browser browser = playwright.firefox().launch();
|
||||
* Page page = browser.newPage();
|
||||
* page.setContent("<div><button>Click me</button></div>");
|
||||
* // Use the selector prefixed with its name.
|
||||
* ElementHandle button = page.querySelector("tag=button");
|
||||
* // Combine it with other selector engines.
|
||||
* page.click("tag=div >> text=\"Click me\"");
|
||||
* // Can use it in any methods supporting selectors.
|
||||
* int buttonCount = (int) page.evalOnSelectorAll("tag=button", "buttons => buttons.length");
|
||||
* browser.close();
|
||||
* }</pre>
|
||||
*
|
||||
* @param name Name that is used in selectors as a prefix, e.g. {@code {name: 'foo'}} enables {@code foo=myselectorbody} selectors. May only
|
||||
* contain {@code [a-zA-Z0-9_]} characters.
|
||||
* @param script Script that evaluates to a selector engine instance.
|
||||
*/
|
||||
void register(String name, Path script, RegisterOptions options);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,14 +16,17 @@
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* - extends: [Error]
|
||||
*
|
||||
* <p> TimeoutError is emitted whenever certain operations are terminated due to timeout, e.g. [{@code method: Page.waitForSelector}]
|
||||
* or [{@code method: BrowserType.launch}].
|
||||
* TimeoutError is emitted whenever certain operations are terminated due to timeout, e.g. {@link Page#waitForSelector
|
||||
* Page.waitForSelector()} or {@link BrowserType#launch BrowserType.launch()}.
|
||||
*/
|
||||
public interface TimeoutError {
|
||||
public class TimeoutError extends PlaywrightException {
|
||||
public TimeoutError(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public TimeoutError(String message, Throwable exception) {
|
||||
super(message, exception);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,9 @@ import java.util.*;
|
||||
|
||||
/**
|
||||
* When browser context is created with the {@code videosPath} option, each page has a video object associated with it.
|
||||
* <pre>{@code
|
||||
* System.out.println(page.video().path());
|
||||
* }</pre>
|
||||
*/
|
||||
public interface Video {
|
||||
/**
|
||||
|
||||
@@ -24,55 +24,83 @@ import java.util.function.Predicate;
|
||||
* The {@code WebSocket} class represents websocket connections in the page.
|
||||
*/
|
||||
public interface WebSocket {
|
||||
interface FrameData {
|
||||
byte[] body();
|
||||
String text();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fired when the websocket closes.
|
||||
*/
|
||||
void onClose(Consumer<WebSocket> handler);
|
||||
/**
|
||||
* Removes handler that was previously added with {@link #onClose onClose(handler)}.
|
||||
*/
|
||||
void offClose(Consumer<WebSocket> handler);
|
||||
|
||||
void onFrameReceived(Consumer<FrameData> handler);
|
||||
void offFrameReceived(Consumer<FrameData> handler);
|
||||
/**
|
||||
* Fired when the websocket recieves a frame.
|
||||
*/
|
||||
void onFrameReceived(Consumer<WebSocketFrame> handler);
|
||||
/**
|
||||
* Removes handler that was previously added with {@link #onFrameReceived onFrameReceived(handler)}.
|
||||
*/
|
||||
void offFrameReceived(Consumer<WebSocketFrame> handler);
|
||||
|
||||
void onFrameSent(Consumer<FrameData> handler);
|
||||
void offFrameSent(Consumer<FrameData> handler);
|
||||
/**
|
||||
* Fired when the websocket sends a frame.
|
||||
*/
|
||||
void onFrameSent(Consumer<WebSocketFrame> handler);
|
||||
/**
|
||||
* Removes handler that was previously added with {@link #onFrameSent onFrameSent(handler)}.
|
||||
*/
|
||||
void offFrameSent(Consumer<WebSocketFrame> handler);
|
||||
|
||||
/**
|
||||
* Fired when the websocket has an error.
|
||||
*/
|
||||
void onSocketError(Consumer<String> handler);
|
||||
/**
|
||||
* Removes handler that was previously added with {@link #onSocketError onSocketError(handler)}.
|
||||
*/
|
||||
void offSocketError(Consumer<String> handler);
|
||||
|
||||
|
||||
class WaitForFrameReceivedOptions {
|
||||
/**
|
||||
* Receives the {@code WebSocketFrame} object and resolves to truthy value when the waiting should resolve.
|
||||
*/
|
||||
public Predicate<WebSocketFrame> predicate;
|
||||
/**
|
||||
* Maximum time to wait for in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
|
||||
* value can be changed by using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()}.
|
||||
*/
|
||||
public Double timeout;
|
||||
public WaitForFrameReceivedOptions withTimeout(double timeout) {
|
||||
|
||||
public WaitForFrameReceivedOptions setPredicate(Predicate<WebSocketFrame> predicate) {
|
||||
this.predicate = predicate;
|
||||
return this;
|
||||
}
|
||||
public WaitForFrameReceivedOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
FrameData waitForFrameReceived(Runnable code, WaitForFrameReceivedOptions options);
|
||||
default FrameData waitForFrameReceived(Runnable code) { return waitForFrameReceived(code, null); }
|
||||
|
||||
class WaitForFrameSentOptions {
|
||||
/**
|
||||
* Receives the {@code WebSocketFrame} object and resolves to truthy value when the waiting should resolve.
|
||||
*/
|
||||
public Predicate<WebSocketFrame> predicate;
|
||||
/**
|
||||
* Maximum time to wait for in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
|
||||
* value can be changed by using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()}.
|
||||
*/
|
||||
public Double timeout;
|
||||
public WaitForFrameSentOptions withTimeout(double timeout) {
|
||||
|
||||
public WaitForFrameSentOptions setPredicate(Predicate<WebSocketFrame> predicate) {
|
||||
this.predicate = predicate;
|
||||
return this;
|
||||
}
|
||||
public WaitForFrameSentOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
FrameData waitForFrameSent(Runnable code, WaitForFrameSentOptions options);
|
||||
default FrameData waitForFrameSent(Runnable code) { return waitForFrameSent(code, null); }
|
||||
|
||||
class WaitForSocketErrorOptions {
|
||||
public Double timeout;
|
||||
public WaitForSocketErrorOptions withTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
String waitForSocketError(Runnable code, WaitForSocketErrorOptions options);
|
||||
default String waitForSocketError(Runnable code) { return waitForSocketError(code, null); }
|
||||
|
||||
/**
|
||||
* Indicates that the web socket has been closed.
|
||||
*/
|
||||
@@ -81,5 +109,41 @@ public interface WebSocket {
|
||||
* Contains the URL of the WebSocket.
|
||||
*/
|
||||
String url();
|
||||
/**
|
||||
* Performs action and waits for a frame to be sent. If predicate is provided, it passes {@code WebSocketFrame} value into the
|
||||
* {@code predicate} function and waits for {@code predicate(webSocketFrame)} to return a truthy value. Will throw an error if the
|
||||
* WebSocket or Page is closed before the frame is received.
|
||||
*
|
||||
* @param callback Callback that performs the action triggering the event.
|
||||
*/
|
||||
default WebSocketFrame waitForFrameReceived(Runnable callback) {
|
||||
return waitForFrameReceived(null, callback);
|
||||
}
|
||||
/**
|
||||
* Performs action and waits for a frame to be sent. If predicate is provided, it passes {@code WebSocketFrame} value into the
|
||||
* {@code predicate} function and waits for {@code predicate(webSocketFrame)} to return a truthy value. Will throw an error if the
|
||||
* WebSocket or Page is closed before the frame is received.
|
||||
*
|
||||
* @param callback Callback that performs the action triggering the event.
|
||||
*/
|
||||
WebSocketFrame waitForFrameReceived(WaitForFrameReceivedOptions options, Runnable callback);
|
||||
/**
|
||||
* Performs action and waits for a frame to be sent. If predicate is provided, it passes {@code WebSocketFrame} value into the
|
||||
* {@code predicate} function and waits for {@code predicate(webSocketFrame)} to return a truthy value. Will throw an error if the
|
||||
* WebSocket or Page is closed before the frame is sent.
|
||||
*
|
||||
* @param callback Callback that performs the action triggering the event.
|
||||
*/
|
||||
default WebSocketFrame waitForFrameSent(Runnable callback) {
|
||||
return waitForFrameSent(null, callback);
|
||||
}
|
||||
/**
|
||||
* Performs action and waits for a frame to be sent. If predicate is provided, it passes {@code WebSocketFrame} value into the
|
||||
* {@code predicate} function and waits for {@code predicate(webSocketFrame)} to return a truthy value. Will throw an error if the
|
||||
* WebSocket or Page is closed before the frame is sent.
|
||||
*
|
||||
* @param callback Callback that performs the action triggering the event.
|
||||
*/
|
||||
WebSocketFrame waitForFrameSent(WaitForFrameSentOptions options, Runnable callback);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 java.util.*;
|
||||
|
||||
/**
|
||||
* The {@code WebSocketFrame} class represents frames sent over {@code WebSocket} connections in the page. Frame payload is returned by
|
||||
* either {@link WebSocketFrame#text WebSocketFrame.text()} or {@link WebSocketFrame#binary WebSocketFrame.binary()} method
|
||||
* depending on the its type.
|
||||
*/
|
||||
public interface WebSocketFrame {
|
||||
/**
|
||||
* Returns binary payload.
|
||||
*/
|
||||
byte[] binary();
|
||||
/**
|
||||
* Returns text payload.
|
||||
*/
|
||||
String text();
|
||||
}
|
||||
|
||||
@@ -20,59 +20,121 @@ import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* The Worker class represents a [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API). {@code worker}
|
||||
* event is emitted on the page object to signal a worker creation. {@code close} event is emitted on the worker object when the
|
||||
* worker is gone.
|
||||
* The Worker class represents a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API">WebWorker</a>.
|
||||
* {@code worker} event is emitted on the page object to signal a worker creation. {@code close} event is emitted on the worker object
|
||||
* when the worker is gone.
|
||||
* <pre>{@code
|
||||
* page.onWorker(worker -> {
|
||||
* System.out.println("Worker created: " + worker.url());
|
||||
* worker.onClose(worker1 -> System.out.println("Worker destroyed: " + worker1.url()));
|
||||
* });
|
||||
* System.out.println("Current workers:");
|
||||
* for (Worker worker : page.workers())
|
||||
* System.out.println(" " + worker.url());
|
||||
* }</pre>
|
||||
*/
|
||||
public interface Worker {
|
||||
|
||||
/**
|
||||
* Emitted when this dedicated <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API">WebWorker</a> is
|
||||
* terminated.
|
||||
*/
|
||||
void onClose(Consumer<Worker> handler);
|
||||
/**
|
||||
* Removes handler that was previously added with {@link #onClose onClose(handler)}.
|
||||
*/
|
||||
void offClose(Consumer<Worker> handler);
|
||||
|
||||
|
||||
class WaitForCloseOptions {
|
||||
/**
|
||||
* Maximum time to wait for in milliseconds. Defaults to {@code 30000} (30 seconds). Pass {@code 0} to disable timeout. The default
|
||||
* value can be changed by using the {@link BrowserContext#setDefaultTimeout BrowserContext.setDefaultTimeout()}.
|
||||
*/
|
||||
public Double timeout;
|
||||
public WaitForCloseOptions withTimeout(double timeout) {
|
||||
|
||||
public WaitForCloseOptions setTimeout(double timeout) {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
Worker waitForClose(Runnable code, WaitForCloseOptions options);
|
||||
default Worker waitForClose(Runnable code) { return waitForClose(code, null); }
|
||||
|
||||
default Object evaluate(String pageFunction) {
|
||||
return evaluate(pageFunction, null);
|
||||
/**
|
||||
* Returns the return value of {@code expression}.
|
||||
*
|
||||
* <p> If the function passed to the {@link Worker#evaluate Worker.evaluate()} returns a <a
|
||||
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, then {@link
|
||||
* Worker#evaluate Worker.evaluate()} would wait for the promise to resolve and return its value.
|
||||
*
|
||||
* <p> If the function passed to the {@link Worker#evaluate Worker.evaluate()} returns a non-[Serializable] value, then {@link
|
||||
* Worker#evaluate Worker.evaluate()} returns {@code undefined}. Playwright also supports transferring some additional values
|
||||
* that are not serializable by {@code JSON}: {@code -0}, {@code NaN}, {@code Infinity}, {@code -Infinity}.
|
||||
*
|
||||
* @param expression JavaScript expression to be evaluated in the browser context. If it looks like a function declaration, it is interpreted
|
||||
* as a function. Otherwise, evaluated as an expression.
|
||||
*/
|
||||
default Object evaluate(String expression) {
|
||||
return evaluate(expression, null);
|
||||
}
|
||||
/**
|
||||
* Returns the return value of {@code pageFunction}
|
||||
* Returns the return value of {@code expression}.
|
||||
*
|
||||
* <p> If the function passed to the {@code worker.evaluate} returns a [Promise], then {@code worker.evaluate} would wait for the promise
|
||||
* to resolve and return its value.
|
||||
* <p> If the function passed to the {@link Worker#evaluate Worker.evaluate()} returns a <a
|
||||
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, then {@link
|
||||
* Worker#evaluate Worker.evaluate()} would wait for the promise to resolve and return its value.
|
||||
*
|
||||
* <p> If the function passed to the {@code worker.evaluate} returns a non-[Serializable] value, then {@code worker.evaluate} returns
|
||||
* {@code undefined}. DevTools Protocol also supports transferring some additional values that are not serializable by {@code JSON}:
|
||||
* {@code -0}, {@code NaN}, {@code Infinity}, {@code -Infinity}, and bigint literals.
|
||||
* <p> If the function passed to the {@link Worker#evaluate Worker.evaluate()} returns a non-[Serializable] value, then {@link
|
||||
* Worker#evaluate Worker.evaluate()} returns {@code undefined}. Playwright also supports transferring some additional values
|
||||
* that are not serializable by {@code JSON}: {@code -0}, {@code NaN}, {@code Infinity}, {@code -Infinity}.
|
||||
*
|
||||
* @param pageFunction Function to be evaluated in the worker context
|
||||
* @param arg Optional argument to pass to {@code pageFunction}
|
||||
* @param expression JavaScript expression to be evaluated in the browser context. If it looks like a function declaration, it is interpreted
|
||||
* as a function. Otherwise, evaluated as an expression.
|
||||
* @param arg Optional argument to pass to {@code expression}.
|
||||
*/
|
||||
Object evaluate(String pageFunction, Object arg);
|
||||
default JSHandle evaluateHandle(String pageFunction) {
|
||||
return evaluateHandle(pageFunction, null);
|
||||
Object evaluate(String expression, Object arg);
|
||||
/**
|
||||
* Returns the return value of {@code expression} as a {@code JSHandle}.
|
||||
*
|
||||
* <p> The only difference between {@link Worker#evaluate Worker.evaluate()} and {@link Worker#evaluateHandle
|
||||
* Worker.evaluateHandle()} is that {@link Worker#evaluateHandle Worker.evaluateHandle()} returns {@code JSHandle}.
|
||||
*
|
||||
* <p> If the function passed to the {@link Worker#evaluateHandle Worker.evaluateHandle()} returns a <a
|
||||
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, then {@link
|
||||
* Worker#evaluateHandle Worker.evaluateHandle()} would wait for the promise to resolve and return its value.
|
||||
*
|
||||
* @param expression JavaScript expression to be evaluated in the browser context. If it looks like a function declaration, it is interpreted
|
||||
* as a function. Otherwise, evaluated as an expression.
|
||||
*/
|
||||
default JSHandle evaluateHandle(String expression) {
|
||||
return evaluateHandle(expression, null);
|
||||
}
|
||||
/**
|
||||
* Returns the return value of {@code pageFunction} as in-page object (JSHandle).
|
||||
* Returns the return value of {@code expression} as a {@code JSHandle}.
|
||||
*
|
||||
* <p> The only difference between {@code worker.evaluate} and {@code worker.evaluateHandle} is that {@code worker.evaluateHandle} returns
|
||||
* in-page object (JSHandle).
|
||||
* <p> The only difference between {@link Worker#evaluate Worker.evaluate()} and {@link Worker#evaluateHandle
|
||||
* Worker.evaluateHandle()} is that {@link Worker#evaluateHandle Worker.evaluateHandle()} returns {@code JSHandle}.
|
||||
*
|
||||
* <p> If the function passed to the {@code worker.evaluateHandle} returns a [Promise], then {@code worker.evaluateHandle} would wait for
|
||||
* the promise to resolve and return its value.
|
||||
* <p> If the function passed to the {@link Worker#evaluateHandle Worker.evaluateHandle()} returns a <a
|
||||
* href='https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise'>Promise</a>, then {@link
|
||||
* Worker#evaluateHandle Worker.evaluateHandle()} would wait for the promise to resolve and return its value.
|
||||
*
|
||||
* @param pageFunction Function to be evaluated in the page context
|
||||
* @param arg Optional argument to pass to {@code pageFunction}
|
||||
* @param expression JavaScript expression to be evaluated in the browser context. If it looks like a function declaration, it is interpreted
|
||||
* as a function. Otherwise, evaluated as an expression.
|
||||
* @param arg Optional argument to pass to {@code expression}.
|
||||
*/
|
||||
JSHandle evaluateHandle(String pageFunction, Object arg);
|
||||
JSHandle evaluateHandle(String expression, Object arg);
|
||||
String url();
|
||||
/**
|
||||
* Performs action and waits for the Worker to close.
|
||||
*
|
||||
* @param callback Callback that performs the action triggering the event.
|
||||
*/
|
||||
default Worker waitForClose(Runnable callback) {
|
||||
return waitForClose(null, callback);
|
||||
}
|
||||
/**
|
||||
* Performs action and waits for the Worker to close.
|
||||
*
|
||||
* @param callback Callback that performs the action triggering the event.
|
||||
*/
|
||||
Worker waitForClose(WaitForCloseOptions options, Runnable callback);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* 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.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Accessibility;
|
||||
import com.microsoft.playwright.AccessibilityNode;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
|
||||
class AccessibilityImpl implements Accessibility {
|
||||
private final PageImpl page;
|
||||
|
||||
AccessibilityImpl(PageImpl page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessibilityNode snapshot(SnapshotOptions options) {
|
||||
return page.withLogging("Accessibility.snapshot", () -> snapshotImpl(options));
|
||||
}
|
||||
|
||||
private AccessibilityNode snapshotImpl(SnapshotOptions options) {
|
||||
if (options == null) {
|
||||
options = new SnapshotOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
JsonObject json = page.sendMessage("accessibilitySnapshot", params).getAsJsonObject();
|
||||
if (!json.has("rootAXNode")) {
|
||||
return null;
|
||||
}
|
||||
return new AccessibilityNodeImpl(json.getAsJsonObject("rootAXNode"));
|
||||
}
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
/*
|
||||
* 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.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.AccessibilityNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class AccessibilityNodeImpl implements AccessibilityNode {
|
||||
private final JsonObject json;
|
||||
|
||||
AccessibilityNodeImpl(JsonObject json) {
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String role() {
|
||||
return json.get("role").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return json.get("name").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String valueString() {
|
||||
if (!json.has("valueString")) {
|
||||
return null;
|
||||
}
|
||||
return json.get("valueString").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double valueNumber() {
|
||||
if (!json.has("valueNumber")) {
|
||||
return null;
|
||||
}
|
||||
return json.get("valueNumber").getAsDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
if (!json.has("description")) {
|
||||
return null;
|
||||
}
|
||||
return json.get("description").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String keyshortcuts() {
|
||||
if (!json.has("keyshortcuts")) {
|
||||
return null;
|
||||
}
|
||||
return json.get("keyshortcuts").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String roledescription() {
|
||||
if (!json.has("roledescription")) {
|
||||
return null;
|
||||
}
|
||||
return json.get("roledescription").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String valuetext() {
|
||||
if (!json.has("valuetext")) {
|
||||
return null;
|
||||
}
|
||||
return json.get("valuetext").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean disabled() {
|
||||
if (!json.has("disabled")) {
|
||||
return null;
|
||||
}
|
||||
return json.get("disabled").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean expanded() {
|
||||
if (!json.has("expanded")) {
|
||||
return null;
|
||||
}
|
||||
return json.get("expanded").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean focused() {
|
||||
if (!json.has("focused")) {
|
||||
return null;
|
||||
}
|
||||
return json.get("focused").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean modal() {
|
||||
if (!json.has("modal")) {
|
||||
return null;
|
||||
}
|
||||
return json.get("modal").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean multiline() {
|
||||
if (!json.has("multiline")) {
|
||||
return null;
|
||||
}
|
||||
return json.get("multiline").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean multiselectable() {
|
||||
if (!json.has("multiselectable")) {
|
||||
return null;
|
||||
}
|
||||
return json.get("multiselectable").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean readonly() {
|
||||
if (!json.has("readonly")) {
|
||||
return null;
|
||||
}
|
||||
return json.get("readonly").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean required() {
|
||||
if (!json.has("required")) {
|
||||
return null;
|
||||
}
|
||||
return json.get("required").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean selected() {
|
||||
if (!json.has("selected")) {
|
||||
return null;
|
||||
}
|
||||
return json.get("selected").getAsBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CheckedState checked() {
|
||||
if (!json.has("checked")) {
|
||||
return null;
|
||||
}
|
||||
String value = json.get("checked").getAsString();
|
||||
switch (value) {
|
||||
case "checked": return CheckedState.CHECKED;
|
||||
case "unchecked": return CheckedState.UNCHECKED;
|
||||
case "mixed": return CheckedState.MIXED;
|
||||
default: throw new IllegalStateException("Unexpected value: " + value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PressedState pressed() {
|
||||
if (!json.has("pressed")) {
|
||||
return null;
|
||||
}
|
||||
String value = json.get("pressed").getAsString();
|
||||
switch (value) {
|
||||
case "pressed": return PressedState.PRESSED;
|
||||
case "released": return PressedState.RELEASED;
|
||||
case "mixed": return PressedState.MIXED;
|
||||
default: throw new IllegalStateException("Unexpected value: " + value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer level() {
|
||||
if (!json.has("level")) {
|
||||
return null;
|
||||
}
|
||||
return json.get("level").getAsInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double valuemin() {
|
||||
if (!json.has("valuemin")) {
|
||||
return null;
|
||||
}
|
||||
return json.get("valuemin").getAsDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double valuemax() {
|
||||
if (!json.has("valuemax")) {
|
||||
return null;
|
||||
}
|
||||
return json.get("valuemax").getAsDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String autocomplete() {
|
||||
if (!json.has("autocomplete")) {
|
||||
return null;
|
||||
}
|
||||
return json.get("autocomplete").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String haspopup() {
|
||||
if (!json.has("haspopup")) {
|
||||
return null;
|
||||
}
|
||||
return json.get("haspopup").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String invalid() {
|
||||
if (!json.has("invalid")) {
|
||||
return null;
|
||||
}
|
||||
return json.get("invalid").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String orientation() {
|
||||
if (!json.has("orientation")) {
|
||||
return null;
|
||||
}
|
||||
return json.get("orientation").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AccessibilityNode> children() {
|
||||
if (!json.has("children")) {
|
||||
return null;
|
||||
}
|
||||
List<AccessibilityNode> result = new ArrayList<>();
|
||||
for (JsonElement e : json.getAsJsonArray("children")) {
|
||||
result.add(new AccessibilityNodeImpl(e.getAsJsonObject()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import com.microsoft.playwright.BrowserContext;
|
||||
import com.microsoft.playwright.Frame;
|
||||
import com.microsoft.playwright.JSHandle;
|
||||
import com.microsoft.playwright.Page;
|
||||
import com.microsoft.playwright.options.BindingCallback;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -29,7 +30,7 @@ import java.util.List;
|
||||
import static com.microsoft.playwright.impl.Serialization.*;
|
||||
|
||||
class BindingCall extends ChannelOwner {
|
||||
private static class SourceImpl implements Page.Binding.Source {
|
||||
private static class SourceImpl implements BindingCallback.Source {
|
||||
private final Frame frame;
|
||||
|
||||
public SourceImpl(Frame frame) {
|
||||
@@ -60,10 +61,10 @@ class BindingCall extends ChannelOwner {
|
||||
return initializer.get("name").getAsString();
|
||||
}
|
||||
|
||||
void call(Page.Binding binding) {
|
||||
void call(BindingCallback binding) {
|
||||
try {
|
||||
Frame frame = connection.getExistingObject(initializer.getAsJsonObject("frame").get("guid").getAsString());
|
||||
Page.Binding.Source source = new SourceImpl(frame);
|
||||
BindingCallback.Source source = new SourceImpl(frame);
|
||||
List<Object> args = new ArrayList<>();
|
||||
if (initializer.has("handle")) {
|
||||
JSHandle handle = connection.getExistingObject(initializer.getAsJsonObject("handle").get("guid").getAsString());
|
||||
|
||||
@@ -20,10 +20,10 @@ import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.*;
|
||||
import com.microsoft.playwright.options.*;
|
||||
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
@@ -31,15 +31,18 @@ import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
import static com.microsoft.playwright.impl.Utils.isFunctionBody;
|
||||
import static com.microsoft.playwright.impl.Utils.isSafeCloseError;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.nio.file.Files.readAllBytes;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
private final BrowserImpl browser;
|
||||
final List<PageImpl> pages = new ArrayList<>();
|
||||
final Router routes = new Router();
|
||||
private boolean isClosedOrClosing;
|
||||
final Map<String, Page.Binding> bindings = new HashMap<>();
|
||||
final Map<String, BindingCallback> bindings = new HashMap<>();
|
||||
PageImpl ownerPage;
|
||||
private final ListenerCollection<EventType> listeners = new ListenerCollection<>();
|
||||
final TimeoutSettings timeoutSettings = new TimeoutSettings();
|
||||
@@ -88,7 +91,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page waitForPage(Runnable code, WaitForPageOptions options) {
|
||||
public Page waitForPage(WaitForPageOptions options, Runnable code) {
|
||||
if (options == null) {
|
||||
options = new WaitForPageOptions();
|
||||
}
|
||||
@@ -99,6 +102,12 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
public void close() {
|
||||
withLogging("BrowserContext.close", () -> closeImpl());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Cookie> cookies(String url) {
|
||||
return cookies(url == null ? emptyList() : asList(url));
|
||||
}
|
||||
|
||||
private void closeImpl() {
|
||||
if (isClosedOrClosing) {
|
||||
return;
|
||||
@@ -114,7 +123,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCookies(List<AddCookie> cookies) {
|
||||
public void addCookies(List<Cookie> cookies) {
|
||||
withLogging("BrowserContext.addCookies", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("cookies", gson().toJsonTree(cookies));
|
||||
@@ -123,15 +132,24 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInitScript(String script, Object arg) {
|
||||
withLogging("BrowserContext.addInitScript", () -> addInitScriptImpl(script, arg));
|
||||
public void addInitScript(String script) {
|
||||
withLogging("BrowserContext.addInitScript", () -> addInitScriptImpl(script));
|
||||
}
|
||||
private void addInitScriptImpl(String script, Object arg) {
|
||||
// TODO: serialize arg
|
||||
|
||||
@Override
|
||||
public void addInitScript(Path path) {
|
||||
withLogging("BrowserContext.addInitScript", () -> {
|
||||
try {
|
||||
byte[] bytes = readAllBytes(path);
|
||||
addInitScriptImpl(new String(bytes, UTF_8));
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to read script from file", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addInitScriptImpl(String script) {
|
||||
JsonObject params = new JsonObject();
|
||||
if (isFunctionBody(script)) {
|
||||
script = "(" + script + ")()";
|
||||
}
|
||||
params.addProperty("source", script);
|
||||
sendMessage("addInitScript", params);
|
||||
}
|
||||
@@ -159,20 +177,20 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
private List<Cookie> cookiesImpl(List<String> urls) {
|
||||
JsonObject params = new JsonObject();
|
||||
if (urls == null) {
|
||||
urls = Collections.emptyList();
|
||||
urls = emptyList();
|
||||
}
|
||||
params.add("urls", gson().toJsonTree(urls));
|
||||
JsonObject json = sendMessage("cookies", params).getAsJsonObject();
|
||||
Cookie[] cookies = gson().fromJson(json.getAsJsonArray("cookies"), Cookie[].class);
|
||||
return Arrays.asList(cookies);
|
||||
return asList(cookies);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exposeBinding(String name, Page.Binding playwrightBinding, ExposeBindingOptions options) {
|
||||
public void exposeBinding(String name, BindingCallback playwrightBinding, ExposeBindingOptions options) {
|
||||
withLogging("BrowserContext.exposeBinding", () -> exposeBindingImpl(name, playwrightBinding, options));
|
||||
}
|
||||
|
||||
private void exposeBindingImpl(String name, Page.Binding playwrightBinding, ExposeBindingOptions options) {
|
||||
private void exposeBindingImpl(String name, BindingCallback playwrightBinding, ExposeBindingOptions options) {
|
||||
if (bindings.containsKey(name)) {
|
||||
throw new PlaywrightException("Function \"" + name + "\" has been already registered");
|
||||
}
|
||||
@@ -192,9 +210,9 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exposeFunction(String name, Page.Function playwrightFunction) {
|
||||
public void exposeFunction(String name, FunctionCallback playwrightFunction) {
|
||||
withLogging("BrowserContext.exposeFunction",
|
||||
() -> exposeBindingImpl(name, (Page.Binding.Source source, Object... args) -> playwrightFunction.call(args), null));
|
||||
() -> exposeBindingImpl(name, (BindingCallback.Source source, Object... args) -> playwrightFunction.call(args), null));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -207,7 +225,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
options = new GrantPermissionsOptions();
|
||||
}
|
||||
if (permissions == null) {
|
||||
permissions = Collections.emptyList();
|
||||
permissions = emptyList();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.add("permissions", gson().toJsonTree(permissions));
|
||||
@@ -315,19 +333,12 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageState storageState(StorageStateOptions options) {
|
||||
public String storageState(StorageStateOptions options) {
|
||||
return withLogging("BrowserContext.storageState", () -> {
|
||||
JsonElement json = sendMessage("storageState");
|
||||
StorageState storageState = gson().fromJson(json, StorageState.class);
|
||||
String storageState = json.toString();
|
||||
if (options != null && options.path != null) {
|
||||
try {
|
||||
Files.createDirectories(options.path.getParent());
|
||||
try (FileWriter writer = new FileWriter(options.path.toFile())) {
|
||||
writer.write(json.toString());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to write storage state to file", e);
|
||||
}
|
||||
Utils.writeToFile(storageState.getBytes(StandardCharsets.UTF_8), options.path);
|
||||
}
|
||||
return storageState;
|
||||
});
|
||||
@@ -370,13 +381,17 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
});
|
||||
}
|
||||
|
||||
void pause() {
|
||||
sendMessage("pause");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleEvent(String event, JsonObject params) {
|
||||
if ("route".equals(event)) {
|
||||
Route route = connection.getExistingObject(params.getAsJsonObject("route").get("guid").getAsString());
|
||||
boolean handled = routes.handle(route);
|
||||
if (!handled) {
|
||||
route.continue_();
|
||||
route.resume();
|
||||
}
|
||||
} else if ("page".equals(event)) {
|
||||
PageImpl page = connection.getExistingObject(params.getAsJsonObject("page").get("guid").getAsString());
|
||||
@@ -384,16 +399,20 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
pages.add(page);
|
||||
} else if ("bindingCall".equals(event)) {
|
||||
BindingCall bindingCall = connection.getExistingObject(params.getAsJsonObject("binding").get("guid").getAsString());
|
||||
Page.Binding binding = bindings.get(bindingCall.name());
|
||||
BindingCallback binding = bindings.get(bindingCall.name());
|
||||
if (binding != null) {
|
||||
bindingCall.call(binding);
|
||||
}
|
||||
} else if ("close".equals(event)) {
|
||||
isClosedOrClosing = true;
|
||||
if (browser != null) {
|
||||
browser.contexts.remove(this);
|
||||
}
|
||||
listeners.notify(EventType.CLOSE, this);
|
||||
didClose();
|
||||
}
|
||||
}
|
||||
|
||||
void didClose() {
|
||||
isClosedOrClosing = true;
|
||||
if (browser != null) {
|
||||
browser.contexts.remove(this);
|
||||
}
|
||||
listeners.notify(EventType.CLOSE, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,13 +16,17 @@
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.*;
|
||||
import com.microsoft.playwright.Browser;
|
||||
import com.microsoft.playwright.BrowserContext;
|
||||
import com.microsoft.playwright.Page;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -34,8 +38,9 @@ import static com.microsoft.playwright.impl.Utils.convertViaJson;
|
||||
import static com.microsoft.playwright.impl.Utils.isSafeCloseError;
|
||||
|
||||
class BrowserImpl extends ChannelOwner implements Browser {
|
||||
final Set<BrowserContext> contexts = new HashSet<>();
|
||||
final Set<BrowserContextImpl> contexts = new HashSet<>();
|
||||
private final ListenerCollection<EventType> listeners = new ListenerCollection<>();
|
||||
public boolean isRemote;
|
||||
private boolean isConnected = true;
|
||||
|
||||
enum EventType {
|
||||
@@ -60,7 +65,17 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
||||
public void close() {
|
||||
withLogging("Browser.close", () -> closeImpl());
|
||||
}
|
||||
|
||||
private void closeImpl() {
|
||||
if (isRemote) {
|
||||
try {
|
||||
connection.close();
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to close browser connection", e);
|
||||
}
|
||||
notifyRemoteClosed();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
sendMessage("close");
|
||||
} catch (PlaywrightException e) {
|
||||
@@ -70,6 +85,17 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
||||
}
|
||||
}
|
||||
|
||||
void notifyRemoteClosed() {
|
||||
// Emulate all pages, contexts and the browser closing upon disconnect.
|
||||
for (BrowserContextImpl context : new ArrayList<>(contexts)) {
|
||||
for (PageImpl page : new ArrayList<>(context.pages)) {
|
||||
page.didClose();
|
||||
}
|
||||
context.didClose();
|
||||
}
|
||||
didClose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BrowserContext> contexts() {
|
||||
return new ArrayList<>(contexts);
|
||||
@@ -90,18 +116,62 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
||||
options = new NewContextOptions();
|
||||
}
|
||||
if (options.storageStatePath != null) {
|
||||
try (FileReader reader = new FileReader(options.storageStatePath.toFile())) {
|
||||
options.storageState = gson().fromJson(reader, BrowserContext.StorageState.class);
|
||||
try {
|
||||
byte[] bytes = Files.readAllBytes(options.storageStatePath);
|
||||
options.storageState = new String(bytes, StandardCharsets.UTF_8);
|
||||
options.storageStatePath = null;
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to read storage state from file", e);
|
||||
}
|
||||
}
|
||||
JsonObject storageState = null;
|
||||
if (options.storageState != null) {
|
||||
storageState = new Gson().fromJson(options.storageState, JsonObject.class);
|
||||
options.storageState = null;
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
if (storageState != null) {
|
||||
params.add("storageState", storageState);
|
||||
}
|
||||
if (options.recordHarPath != null) {
|
||||
JsonObject recordHar = new JsonObject();
|
||||
recordHar.addProperty("path", options.recordHarPath.toString());
|
||||
if (options.recordHarOmitContent != null) {
|
||||
recordHar.addProperty("omitContent", true);
|
||||
}
|
||||
params.remove("recordHarPath");
|
||||
params.remove("recordHarOmitContent");
|
||||
params.add("recordHar", recordHar);
|
||||
} else if (options.recordHarOmitContent != null) {
|
||||
throw new PlaywrightException("recordHarOmitContent is set but recordHarPath is null");
|
||||
}
|
||||
if (options.recordVideoDir != null) {
|
||||
JsonObject recordVideo = new JsonObject();
|
||||
recordVideo.addProperty("dir", options.recordVideoDir.toString());
|
||||
if (options.recordVideoSize != null) {
|
||||
recordVideo.add("size", gson().toJsonTree(options.recordVideoSize));
|
||||
}
|
||||
params.remove("recordVideoDir");
|
||||
params.remove("recordVideoSize");
|
||||
params.add("recordVideo", recordVideo);
|
||||
} else if (options.recordVideoSize != null) {
|
||||
throw new PlaywrightException("recordVideoSize is set but recordVideoDir is null");
|
||||
}
|
||||
if (options.viewportSize != null) {
|
||||
if (options.viewportSize.isPresent()) {
|
||||
JsonElement size = params.get("viewportSize");
|
||||
params.remove("viewportSize");
|
||||
params.add("viewport", size);
|
||||
} else {
|
||||
params.remove("viewportSize");
|
||||
params.addProperty("noDefaultViewport", true);
|
||||
}
|
||||
}
|
||||
params.addProperty("sdkLanguage", "java");
|
||||
JsonElement result = sendMessage("newContext", params);
|
||||
BrowserContextImpl context = connection.getExistingObject(result.getAsJsonObject().getAsJsonObject("context").get("guid").getAsString());
|
||||
if (options.recordVideo != null) {
|
||||
context.videosDir = options.recordVideo.dir;
|
||||
if (options.recordVideoDir != null) {
|
||||
context.videosDir = options.recordVideoDir;
|
||||
}
|
||||
contexts.add(context);
|
||||
return context;
|
||||
@@ -136,8 +206,12 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
||||
@Override
|
||||
void handleEvent(String event, JsonObject parameters) {
|
||||
if ("close".equals(event)) {
|
||||
isConnected = false;
|
||||
listeners.notify(EventType.DISCONNECTED, this);
|
||||
didClose();
|
||||
}
|
||||
}
|
||||
|
||||
private void didClose() {
|
||||
isConnected = false;
|
||||
listeners.notify(EventType.DISCONNECTED, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,13 +18,18 @@ package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.BrowserContext;
|
||||
import com.microsoft.playwright.Browser;
|
||||
import com.microsoft.playwright.BrowserType;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
import static com.microsoft.playwright.impl.Serialization.toProtocol;
|
||||
|
||||
class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||
BrowserTypeImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
@@ -45,6 +50,47 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||
return connection.getExistingObject(result.getAsJsonObject().getAsJsonObject("browser").get("guid").getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Browser connect(String wsEndpoint, ConnectOptions options) {
|
||||
return withLogging("BrowserType.connect", () -> connectImpl(wsEndpoint, options));
|
||||
}
|
||||
|
||||
private Browser connectImpl(String wsEndpoint, ConnectOptions options) {
|
||||
try {
|
||||
Duration timeout = Duration.ofDays(1);
|
||||
if (options != null && options.timeout != null) {
|
||||
timeout = Duration.ofMillis(Math.round(options.timeout));
|
||||
}
|
||||
WebSocketTransport transport = new WebSocketTransport(new URI(wsEndpoint), timeout);
|
||||
Connection connection = new Connection(transport);
|
||||
RemoteBrowser remoteBrowser = (RemoteBrowser) connection.waitForObjectWithKnownName("remoteBrowser");
|
||||
PlaywrightImpl playwright = this.connection.getExistingObject("Playwright");
|
||||
SelectorsImpl selectors = remoteBrowser.selectors();
|
||||
playwright.sharedSelectors.addChannel(selectors);
|
||||
BrowserImpl browser = remoteBrowser.browser();
|
||||
browser.isRemote = true;
|
||||
Consumer<WebSocketTransport> connectionCloseListener = new Consumer<WebSocketTransport>() {
|
||||
@Override
|
||||
public void accept(WebSocketTransport t) {
|
||||
browser.notifyRemoteClosed();
|
||||
}
|
||||
};
|
||||
transport.onClose(connectionCloseListener);
|
||||
browser.onDisconnected(b -> {
|
||||
playwright.sharedSelectors.removeChannel(selectors);
|
||||
transport.offClose(connectionCloseListener);
|
||||
try {
|
||||
connection.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
});
|
||||
return browser;
|
||||
} catch (URISyntaxException e) {
|
||||
throw new PlaywrightException("Failed to connect", e);
|
||||
}
|
||||
}
|
||||
|
||||
public String executablePath() {
|
||||
return initializer.get("executablePath").getAsString();
|
||||
}
|
||||
@@ -61,10 +107,45 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("userDataDir", userDataDir.toString());
|
||||
if (options.recordHarPath != null) {
|
||||
JsonObject recordHar = new JsonObject();
|
||||
recordHar.addProperty("path", options.recordHarPath.toString());
|
||||
if (options.recordHarOmitContent != null) {
|
||||
recordHar.addProperty("omitContent", true);
|
||||
}
|
||||
params.remove("recordHarPath");
|
||||
params.remove("recordHarOmitContent");
|
||||
params.add("recordHar", recordHar);
|
||||
} else if (options.recordHarOmitContent != null) {
|
||||
throw new PlaywrightException("recordHarOmitContent is set but recordHarPath is null");
|
||||
}
|
||||
if (options.recordVideoDir != null) {
|
||||
JsonObject recordVideo = new JsonObject();
|
||||
recordVideo.addProperty("dir", options.recordVideoDir.toString());
|
||||
if (options.recordVideoSize != null) {
|
||||
recordVideo.add("size", gson().toJsonTree(options.recordVideoSize));
|
||||
}
|
||||
params.remove("recordVideoDir");
|
||||
params.remove("recordVideoSize");
|
||||
params.add("recordVideo", recordVideo);
|
||||
} else if (options.recordVideoSize != null) {
|
||||
throw new PlaywrightException("recordVideoSize is set but recordVideoDir is null");
|
||||
}
|
||||
if (options.viewportSize != null) {
|
||||
if (options.viewportSize.isPresent()) {
|
||||
JsonElement size = params.get("viewportSize");
|
||||
params.remove("viewportSize");
|
||||
params.add("viewport", size);
|
||||
} else {
|
||||
params.remove("viewportSize");
|
||||
params.addProperty("noDefaultViewport", true);
|
||||
}
|
||||
}
|
||||
params.addProperty("sdkLanguage", "java");
|
||||
JsonObject json = sendMessage("launchPersistentContext", params).getAsJsonObject();
|
||||
BrowserContextImpl context = connection.getExistingObject(json.getAsJsonObject("context").get("guid").getAsString());
|
||||
if (options.recordVideo != null) {
|
||||
context.videosDir = options.recordVideo.dir;
|
||||
if (options.recordVideoDir != null) {
|
||||
context.videosDir = options.recordVideoDir;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -16,13 +16,25 @@
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
import com.microsoft.playwright.TimeoutError;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
|
||||
@@ -53,6 +65,7 @@ public class Connection {
|
||||
private final Map<String, ChannelOwner> objects = new HashMap<>();
|
||||
private final Root root;
|
||||
private int lastId = 0;
|
||||
private final Path srcDir;
|
||||
private final Map<Integer, WaitableResult<JsonElement>> callbacks = new HashMap<>();
|
||||
|
||||
class Root extends ChannelOwner {
|
||||
@@ -61,9 +74,18 @@ public class Connection {
|
||||
}
|
||||
}
|
||||
|
||||
public Connection(InputStream in, OutputStream out) {
|
||||
transport = new Transport(in, out);
|
||||
Connection(Transport transport) {
|
||||
this.transport = transport;
|
||||
root = new Root(this);
|
||||
String srcRoot = System.getenv("PLAYWRIGHT_JAVA_SRC");
|
||||
if (srcRoot == null) {
|
||||
srcDir = null;
|
||||
} else {
|
||||
srcDir = Paths.get(srcRoot);
|
||||
if (!Files.exists(srcDir)) {
|
||||
throw new PlaywrightException("PLAYWRIGHT_JAVA_SRC environment variable points to non-existing location: '" + srcRoot + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void close() throws IOException {
|
||||
@@ -78,6 +100,45 @@ public class Connection {
|
||||
return internalSendMessage(guid, method, params);
|
||||
}
|
||||
|
||||
private String sourceFile(StackTraceElement frame) {
|
||||
String pkg = frame.getClassName();
|
||||
int lastDot = pkg.lastIndexOf('.');
|
||||
if (lastDot == -1) {
|
||||
pkg = "";
|
||||
} else {
|
||||
pkg = frame.getClassName().substring(0, lastDot + 1);
|
||||
}
|
||||
pkg = pkg.replace('.', File.separatorChar);
|
||||
return srcDir.resolve(pkg).resolve(frame.getFileName()).toString();
|
||||
}
|
||||
|
||||
private JsonArray currentStackTrace() {
|
||||
StackTraceElement[] stack = Thread.currentThread().getStackTrace();
|
||||
|
||||
int index = 0;
|
||||
while (index < stack.length && !stack[index].getClassName().equals(getClass().getName())) {
|
||||
index++;
|
||||
};
|
||||
// Find Playwright API call
|
||||
while (index < stack.length && stack[index].getClassName().startsWith("com.microsoft.playwright.")) {
|
||||
// hack for tests
|
||||
if (stack[index].getClassName().startsWith("com.microsoft.playwright.Test")) {
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
JsonArray jsonStack = new JsonArray();
|
||||
for (; index < stack.length; index++) {
|
||||
StackTraceElement frame = stack[index];
|
||||
JsonObject jsonFrame = new JsonObject();
|
||||
jsonFrame.addProperty("file", sourceFile(frame));
|
||||
jsonFrame.addProperty("line", frame.getLineNumber());
|
||||
jsonFrame.addProperty("function", frame.getClassName() + "." + frame.getMethodName());
|
||||
jsonStack.add(jsonFrame);
|
||||
}
|
||||
return jsonStack;
|
||||
}
|
||||
|
||||
private WaitableResult<JsonElement> internalSendMessage(String guid, String method, JsonObject params) {
|
||||
int id = ++lastId;
|
||||
WaitableResult<JsonElement> result = new WaitableResult<>();
|
||||
@@ -87,6 +148,11 @@ public class Connection {
|
||||
message.addProperty("guid", guid);
|
||||
message.addProperty("method", method);
|
||||
message.add("params", params);
|
||||
if (srcDir != null) {
|
||||
JsonObject metadata = new JsonObject();
|
||||
metadata.add("stack", currentStackTrace());
|
||||
message.add("metadata", metadata);
|
||||
}
|
||||
transport.send(gson().toJson(message));
|
||||
return result;
|
||||
}
|
||||
@@ -135,10 +201,12 @@ public class Connection {
|
||||
if (message.error == null) {
|
||||
callback.complete(message.result);
|
||||
} else {
|
||||
if (message.error.error != null) {
|
||||
callback.completeExceptionally(new ServerException(message.error.error));
|
||||
} else {
|
||||
if (message.error.error == null) {
|
||||
callback.completeExceptionally(new PlaywrightException(message.error.toString()));
|
||||
} else if ("TimeoutError".equals(message.error.error.name)) {
|
||||
callback.completeExceptionally(new TimeoutError(message.error.error.toString()));
|
||||
} else {
|
||||
callback.completeExceptionally(new DriverException(message.error.error));
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -229,6 +297,9 @@ public class Connection {
|
||||
case "Request":
|
||||
result = new RequestImpl(parent, type, guid, initializer);
|
||||
break;
|
||||
case "RemoteBrowser":
|
||||
result = new RemoteBrowser(parent, type, guid, initializer);
|
||||
break;
|
||||
case "Response":
|
||||
result = new ResponseImpl(parent, type, guid, initializer);
|
||||
break;
|
||||
|
||||
@@ -48,7 +48,11 @@ public class ConsoleMessageImpl extends ChannelOwner implements ConsoleMessage {
|
||||
return result;
|
||||
}
|
||||
|
||||
public Location location() {
|
||||
return gson().fromJson(initializer.get("location"), Location.class);
|
||||
@Override
|
||||
public String location() {
|
||||
JsonObject location = initializer.getAsJsonObject("location");
|
||||
return location.get("url").getAsString() + ":" +
|
||||
location.get("lineNumber").getAsNumber() + ":" +
|
||||
location.get("columnNumber").getAsNumber();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* 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.BrowserType;
|
||||
import com.microsoft.playwright.DeviceDescriptor;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
|
||||
class DeviceDescriptorImpl implements DeviceDescriptor {
|
||||
PlaywrightImpl playwright;
|
||||
private static class ViewportImpl implements Viewport{
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
@Override
|
||||
public int width() {
|
||||
return width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int height() {
|
||||
return height;
|
||||
}
|
||||
}
|
||||
private ViewportImpl viewport;
|
||||
private String userAgent;
|
||||
private double deviceScaleFactor;
|
||||
private boolean isMobile;
|
||||
private boolean hasTouch;
|
||||
private String defaultBrowserType;
|
||||
|
||||
@Override
|
||||
public Viewport viewport() {
|
||||
return viewport;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String userAgent() {
|
||||
return userAgent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double deviceScaleFactor() {
|
||||
return deviceScaleFactor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMobile() {
|
||||
return isMobile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTouch() {
|
||||
return hasTouch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BrowserType defaultBrowserType() {
|
||||
switch (defaultBrowserType) {
|
||||
case "chromium": return playwright.chromium();
|
||||
case "firefox": return playwright.firefox();
|
||||
case "webkit": return playwright.webkit();
|
||||
default: throw new PlaywrightException("Unknown type: " + defaultBrowserType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@ package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Dialog;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
|
||||
class DialogImpl extends ChannelOwner implements Dialog {
|
||||
DialogImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
@@ -52,13 +51,7 @@ class DialogImpl extends ChannelOwner implements Dialog {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type type() {
|
||||
switch (initializer.get("type").getAsString()) {
|
||||
case "alert": return Type.ALERT;
|
||||
case "beforeunload": return Type.BEFOREUNLOAD;
|
||||
case "confirm": return Type.CONFIRM;
|
||||
case "prompt": return Type.PROMPT;
|
||||
default: throw new PlaywrightException("Unexpected dialog type: " + initializer.get("type").getAsString());
|
||||
}
|
||||
public String type() {
|
||||
return initializer.get("type").getAsString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,16 +16,27 @@
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.BrowserContext;
|
||||
import com.microsoft.playwright.Download;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static com.microsoft.playwright.impl.Utils.writeToFile;
|
||||
|
||||
public class DownloadImpl extends ChannelOwner implements Download {
|
||||
private final BrowserImpl browser;
|
||||
|
||||
public DownloadImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
browser = ((BrowserContextImpl) parent).browser();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -71,6 +82,9 @@ public class DownloadImpl extends ChannelOwner implements Download {
|
||||
@Override
|
||||
public Path path() {
|
||||
return withLogging("Download.path", () -> {
|
||||
if (browser != null && browser.isRemote) {
|
||||
throw new PlaywrightException("Path is not available when using browserType.connect(). Use download.saveAs() to save a local copy.");
|
||||
}
|
||||
JsonObject json = sendMessage("path").getAsJsonObject();
|
||||
return FileSystems.getDefault().getPath(json.get("value").getAsString());
|
||||
});
|
||||
@@ -79,6 +93,13 @@ public class DownloadImpl extends ChannelOwner implements Download {
|
||||
@Override
|
||||
public void saveAs(Path path) {
|
||||
withLogging("Download.saveAs", () -> {
|
||||
if (browser != null && browser.isRemote) {
|
||||
JsonObject jsonObject = sendMessage("saveAsStream").getAsJsonObject();
|
||||
Stream stream = connection.getExistingObject(jsonObject.getAsJsonObject("stream").get("guid").getAsString());
|
||||
writeToFile(stream.stream(), path);
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("path", path.toString());
|
||||
sendMessage("saveAs", params);
|
||||
|
||||
+12
-14
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
* <p>
|
||||
*
|
||||
* 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
|
||||
* <p>
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
*
|
||||
* 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.
|
||||
@@ -14,17 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright;
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
public interface DeviceDescriptor {
|
||||
interface Viewport {
|
||||
int width();
|
||||
int height();
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
class DriverException extends PlaywrightException {
|
||||
DriverException(SerializedError.Error error) {
|
||||
super(error.toString());
|
||||
}
|
||||
Viewport viewport();
|
||||
String userAgent();
|
||||
double deviceScaleFactor();
|
||||
boolean isMobile();
|
||||
boolean hasTouch();
|
||||
BrowserType defaultBrowserType();
|
||||
}
|
||||
@@ -22,14 +22,20 @@ import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.ElementHandle;
|
||||
import com.microsoft.playwright.FileChooser;
|
||||
import com.microsoft.playwright.Frame;
|
||||
import com.microsoft.playwright.options.BoundingBox;
|
||||
import com.microsoft.playwright.options.ElementState;
|
||||
import com.microsoft.playwright.options.FilePayload;
|
||||
import com.microsoft.playwright.options.SelectOption;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.*;
|
||||
import static com.microsoft.playwright.impl.Utils.isFunctionBody;
|
||||
import static com.microsoft.playwright.options.ScreenshotType.JPEG;
|
||||
import static com.microsoft.playwright.options.ScreenshotType.PNG;
|
||||
|
||||
public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
ElementHandleImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
@@ -79,7 +85,6 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.addProperty("isFunction", isFunctionBody(pageFunction));
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evalOnSelector", params);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
@@ -93,7 +98,6 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.addProperty("isFunction", isFunctionBody(pageFunction));
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evalOnSelectorAll", params);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
@@ -135,16 +139,6 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
options = new ClickOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.remove("button");
|
||||
if (options.button != null) {
|
||||
params.addProperty("button", Serialization.toProtocol(options.button));
|
||||
}
|
||||
|
||||
params.remove("modifiers");
|
||||
if (options.modifiers != null) {
|
||||
params.add("modifiers", Serialization.toProtocol(options.modifiers));
|
||||
}
|
||||
|
||||
sendMessage("click", params);
|
||||
}
|
||||
|
||||
@@ -171,16 +165,6 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
options = new DblclickOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.remove("button");
|
||||
if (options.button != null) {
|
||||
params.addProperty("button", Serialization.toProtocol(options.button));
|
||||
}
|
||||
|
||||
params.remove("modifiers");
|
||||
if (options.modifiers != null) {
|
||||
params.add("modifiers", Serialization.toProtocol(options.modifiers));
|
||||
}
|
||||
|
||||
sendMessage("dblclick", params);
|
||||
}
|
||||
|
||||
@@ -227,10 +211,6 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
public void hover(HoverOptions options) {
|
||||
withLogging("ElementHandle.hover", () -> {
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.remove("modifiers");
|
||||
if (options.modifiers != null) {
|
||||
params.add("modifiers", Serialization.toProtocol(options.modifiers));
|
||||
}
|
||||
sendMessage("hover", params);
|
||||
});
|
||||
}
|
||||
@@ -323,10 +303,6 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
sendMessage("press", params);
|
||||
}
|
||||
|
||||
private static String toProtocol(ScreenshotOptions.Type type) {
|
||||
return type.toString().toLowerCase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] screenshot(ScreenshotOptions options) {
|
||||
return withLogging("ElementHandle.screenshot", () -> screenshotImpl(options));
|
||||
@@ -337,21 +313,19 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
options = new ScreenshotOptions();
|
||||
}
|
||||
if (options.type == null) {
|
||||
options.type = ScreenshotOptions.Type.PNG;
|
||||
options.type = PNG;
|
||||
if (options.path != null) {
|
||||
String fileName = options.path.getFileName().toString();
|
||||
int extStart = fileName.lastIndexOf('.');
|
||||
if (extStart != -1) {
|
||||
String extension = fileName.substring(extStart).toLowerCase();
|
||||
if (".jpeg".equals(extension) || ".jpg".equals(extension)) {
|
||||
options.type = ScreenshotOptions.Type.JPEG;
|
||||
options.type = JPEG;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.remove("type");
|
||||
params.addProperty("type", toProtocol(options.type));
|
||||
params.remove("path");
|
||||
JsonObject json = sendMessage("screenshot", params).getAsJsonObject();
|
||||
|
||||
@@ -367,6 +341,33 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
withLogging("ElementHandle.scrollIntoViewIfNeeded", () -> scrollIntoViewIfNeededImpl(options));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String value, SelectOptionOptions options) {
|
||||
String[] values = value == null ? null : new String[]{ value };
|
||||
return selectOption(values, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(ElementHandle value, SelectOptionOptions options) {
|
||||
ElementHandle[] values = value == null ? null : new ElementHandle[]{ value };
|
||||
return selectOption(values, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String[] values, SelectOptionOptions options) {
|
||||
if (values == null) {
|
||||
return selectOption(new SelectOption[0], options);
|
||||
}
|
||||
return selectOption(Arrays.asList(values).stream().map(
|
||||
v -> new SelectOption().setValue(v)).toArray(SelectOption[]::new), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(SelectOption value, SelectOptionOptions options) {
|
||||
SelectOption[] values = value == null ? null : new SelectOption[]{ value };
|
||||
return selectOption(values, options);
|
||||
}
|
||||
|
||||
private void scrollIntoViewIfNeededImpl(ScrollIntoViewIfNeededOptions options) {
|
||||
if (options == null) {
|
||||
options = new ScrollIntoViewIfNeededOptions();
|
||||
@@ -400,7 +401,7 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
}
|
||||
|
||||
private List<String> selectOption(JsonObject params) {
|
||||
return withLogging("ElementHandle.selectOption", () -> {
|
||||
return withLogging("SelectOption", () -> {
|
||||
JsonObject json = sendMessage("selectOption", params).getAsJsonObject();
|
||||
return parseStringList(json.getAsJsonArray("values"));
|
||||
});
|
||||
@@ -411,6 +412,11 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
withLogging("ElementHandle.selectText", () -> selectTextImpl(options));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(Path files, SetInputFilesOptions options) {
|
||||
setInputFiles(new Path[]{files}, options);
|
||||
}
|
||||
|
||||
private void selectTextImpl(SelectTextOptions options) {
|
||||
if (options == null) {
|
||||
options = new SelectTextOptions();
|
||||
@@ -425,11 +431,16 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(FileChooser.FilePayload[] files, SetInputFilesOptions options) {
|
||||
public void setInputFiles(FilePayload files, SetInputFilesOptions options) {
|
||||
setInputFiles(new FilePayload[]{files}, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(FilePayload[] files, SetInputFilesOptions options) {
|
||||
withLogging("ElementHandle.setInputFiles", () -> setInputFilesImpl(files, options));
|
||||
}
|
||||
|
||||
void setInputFilesImpl(FileChooser.FilePayload[] files, SetInputFilesOptions options) {
|
||||
void setInputFilesImpl(FilePayload[] files, SetInputFilesOptions options) {
|
||||
if (options == null) {
|
||||
options = new SetInputFilesOptions();
|
||||
}
|
||||
@@ -448,10 +459,6 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
options = new TapOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
if (options.modifiers != null) {
|
||||
params.remove("modifiers");
|
||||
params.add("modifiers", Serialization.toProtocol(options.modifiers));
|
||||
}
|
||||
sendMessage("tap", params);
|
||||
}
|
||||
|
||||
@@ -503,15 +510,15 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
if (options == null) {
|
||||
options = new WaitForElementStateOptions();
|
||||
}
|
||||
if (state == null) {
|
||||
throw new IllegalArgumentException("State cannot be null");
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("state", toProtocol(state));
|
||||
sendMessage("waitForElementState", params);
|
||||
}
|
||||
|
||||
private static String toProtocol(ElementState state) {
|
||||
if (state == null) {
|
||||
throw new IllegalArgumentException("State cannot by null");
|
||||
}
|
||||
return state.toString().toLowerCase();
|
||||
}
|
||||
|
||||
@@ -525,8 +532,6 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
options = new WaitForSelectorOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.remove("state");
|
||||
params.addProperty("state", toProtocol(options.state));
|
||||
params.addProperty("selector", selector);
|
||||
JsonElement json = sendMessage("waitForSelector", params);
|
||||
JsonObject element = json.getAsJsonObject().getAsJsonObject("element");
|
||||
@@ -535,11 +540,4 @@ public class ElementHandleImpl extends JSHandleImpl implements ElementHandle {
|
||||
}
|
||||
return connection.getExistingObject(element.get("guid").getAsString());
|
||||
}
|
||||
|
||||
private static String toProtocol(WaitForSelectorOptions.State state) {
|
||||
if (state == null) {
|
||||
state = WaitForSelectorOptions.State.VISIBLE;
|
||||
}
|
||||
return state.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.microsoft.playwright.impl;
|
||||
import com.microsoft.playwright.ElementHandle;
|
||||
import com.microsoft.playwright.FileChooser;
|
||||
import com.microsoft.playwright.Page;
|
||||
import com.microsoft.playwright.options.FilePayload;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
@@ -50,11 +51,21 @@ class FileChooserImpl implements FileChooser {
|
||||
return page;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFiles(Path files, SetFilesOptions options) {
|
||||
setFiles(new Path[]{files}, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFiles(Path[] files, SetFilesOptions options) {
|
||||
setFiles(Utils.toFilePayloads(files), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFiles(FilePayload files, SetFilesOptions options) {
|
||||
setFiles(new FilePayload[]{files}, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFiles(FilePayload[] files, SetFilesOptions options) {
|
||||
page.withLogging("FileChooser.setInputFiles",
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.*;
|
||||
import com.microsoft.playwright.options.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -28,10 +29,9 @@ import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.microsoft.playwright.Frame.LoadState.*;
|
||||
import static com.microsoft.playwright.options.LoadState.*;
|
||||
import static com.microsoft.playwright.impl.Serialization.*;
|
||||
import static com.microsoft.playwright.impl.Utils.convertViaJson;
|
||||
import static com.microsoft.playwright.impl.Utils.isFunctionBody;
|
||||
|
||||
public class FrameImpl extends ChannelOwner implements Frame {
|
||||
private String name;
|
||||
@@ -88,6 +88,33 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
return withLogging("Frame.querySelectorAll", () -> querySelectorAllImpl(selector));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String selector, String value, SelectOptionOptions options) {
|
||||
String[] values = value == null ? null : new String[]{ value };
|
||||
return selectOption(selector, values, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String selector, ElementHandle value, SelectOptionOptions options) {
|
||||
ElementHandle[] values = value == null ? null : new ElementHandle[]{value};
|
||||
return selectOption(selector, values, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String selector, String[] values, SelectOptionOptions options) {
|
||||
if (values == null) {
|
||||
return selectOption(selector, new SelectOption[0], options);
|
||||
}
|
||||
return selectOption(selector, Arrays.asList(values).stream().map(
|
||||
v -> new SelectOption().setValue(v)).toArray(SelectOption[]::new), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String selector, SelectOption value, SelectOptionOptions options) {
|
||||
SelectOption[] values = value == null ? null : new SelectOption[]{value};
|
||||
return selectOption(selector, values, options);
|
||||
}
|
||||
|
||||
List<ElementHandle> querySelectorAllImpl(String selector) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
@@ -112,7 +139,6 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.addProperty("isFunction", isFunctionBody(pageFunction));
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evalOnSelector", params);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
@@ -128,7 +154,6 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.addProperty("isFunction", isFunctionBody(pageFunction));
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evalOnSelectorAll", params);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
@@ -217,17 +242,6 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
|
||||
params.remove("button");
|
||||
if (options.button != null) {
|
||||
params.addProperty("button", Serialization.toProtocol(options.button));
|
||||
}
|
||||
|
||||
params.remove("modifiers");
|
||||
if (options.modifiers != null) {
|
||||
params.add("modifiers", Serialization.toProtocol(options.modifiers));
|
||||
}
|
||||
|
||||
sendMessage("click", params);
|
||||
}
|
||||
|
||||
@@ -251,17 +265,6 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
|
||||
params.remove("button");
|
||||
if (options.button != null) {
|
||||
params.addProperty("button", Serialization.toProtocol(options.button));
|
||||
}
|
||||
|
||||
params.remove("modifiers");
|
||||
if (options.modifiers != null) {
|
||||
params.add("modifiers", Serialization.toProtocol(options.modifiers));
|
||||
}
|
||||
|
||||
sendMessage("dblclick", params);
|
||||
}
|
||||
|
||||
@@ -290,7 +293,6 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", expression);
|
||||
params.addProperty("world", "main");
|
||||
params.addProperty("isFunction", isFunctionBody(expression));
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evaluateExpression", params);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
@@ -306,7 +308,6 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.addProperty("world", "main");
|
||||
params.addProperty("isFunction", isFunctionBody(pageFunction));
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evaluateExpressionHandle", params);
|
||||
return connection.getExistingObject(json.getAsJsonObject().getAsJsonObject("handle").get("guid").getAsString());
|
||||
@@ -382,10 +383,6 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("url", url);
|
||||
if (options.waitUntil != null) {
|
||||
params.remove("waitUntil");
|
||||
params.addProperty("waitUntil", toProtocol(options.waitUntil));
|
||||
}
|
||||
JsonElement result = sendMessage("goto", params);
|
||||
JsonObject jsonResponse = result.getAsJsonObject().getAsJsonObject("response");
|
||||
if (jsonResponse == null) {
|
||||
@@ -564,11 +561,11 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String selector, ElementHandle.SelectOption[] values, SelectOptionOptions options) {
|
||||
public List<String> selectOption(String selector, SelectOption[] values, SelectOptionOptions options) {
|
||||
return withLogging("Frame.selectOption", () -> selectOptionImpl(selector, values, options));
|
||||
}
|
||||
|
||||
List<String> selectOptionImpl(String selector, ElementHandle.SelectOption[] values, SelectOptionOptions options) {
|
||||
List<String> selectOptionImpl(String selector, SelectOption[] values, SelectOptionOptions options) {
|
||||
if (options == null) {
|
||||
options = new SelectOptionOptions();
|
||||
}
|
||||
@@ -602,31 +599,22 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
return parseStringList(json.getAsJsonArray("values"));
|
||||
}
|
||||
|
||||
static String toProtocol(LoadState waitUntil) {
|
||||
if (waitUntil == null) {
|
||||
waitUntil = LoadState.LOAD;
|
||||
}
|
||||
switch (waitUntil) {
|
||||
case DOMCONTENTLOADED: return "domcontentloaded";
|
||||
case LOAD: return "load";
|
||||
case NETWORKIDLE: return "networkidle";
|
||||
default: throw new PlaywrightException("Unexpected value: " + waitUntil);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContent(String html, SetContentOptions options) {
|
||||
withLogging("Frame.setContent", () -> setContentImpl(html, options));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(String selector, Path files, SetInputFilesOptions options) {
|
||||
setInputFiles(selector, new Path[] {files}, options);
|
||||
}
|
||||
|
||||
void setContentImpl(String html, SetContentOptions options) {
|
||||
if (options == null) {
|
||||
options = new SetContentOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("html", html);
|
||||
params.remove("waitUntil");
|
||||
params.addProperty("waitUntil", toProtocol(options.waitUntil));
|
||||
sendMessage("setContent", params);
|
||||
}
|
||||
|
||||
@@ -635,16 +623,21 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
withLogging("Frame.setInputFiles", () -> setInputFilesImpl(selector, files, options));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(String selector, FilePayload files, SetInputFilesOptions options) {
|
||||
setInputFiles(selector, new FilePayload[]{files}, options);
|
||||
}
|
||||
|
||||
void setInputFilesImpl(String selector, Path[] files, SetInputFilesOptions options) {
|
||||
setInputFiles(selector, Utils.toFilePayloads(files), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(String selector, FileChooser.FilePayload[] files, SetInputFilesOptions options) {
|
||||
public void setInputFiles(String selector, FilePayload[] files, SetInputFilesOptions options) {
|
||||
withLogging("Frame.setInputFiles", () -> setInputFilesImpl(selector, files, options));
|
||||
}
|
||||
|
||||
void setInputFilesImpl(String selector, FileChooser.FilePayload[] files, SetInputFilesOptions options) {
|
||||
void setInputFilesImpl(String selector, FilePayload[] files, SetInputFilesOptions options) {
|
||||
if (options == null) {
|
||||
options = new SetInputFilesOptions();
|
||||
}
|
||||
@@ -663,10 +656,6 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
options = new TapOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.remove("modifiers");
|
||||
if (options.modifiers != null) {
|
||||
params.add("modifiers", Serialization.toProtocol(options.modifiers));
|
||||
}
|
||||
params.addProperty("selector", selector);
|
||||
sendMessage("tap", params);
|
||||
}
|
||||
@@ -740,7 +729,6 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.addProperty("isFunction", isFunctionBody(pageFunction));
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("waitForFunction", params);
|
||||
JsonObject element = json.getAsJsonObject().getAsJsonObject("handle");
|
||||
@@ -867,7 +855,7 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response waitForNavigation(Runnable code, WaitForNavigationOptions options) {
|
||||
public Response waitForNavigation(WaitForNavigationOptions options, Runnable code) {
|
||||
return withLogging("Frame.waitForNavigation", () -> waitForNavigationImpl(code, options));
|
||||
}
|
||||
|
||||
@@ -876,22 +864,18 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
options = new WaitForNavigationOptions();
|
||||
}
|
||||
if (options.waitUntil == null) {
|
||||
options.waitUntil = LOAD;
|
||||
options.waitUntil = WaitUntilState.LOAD;
|
||||
}
|
||||
|
||||
List<Waitable<Response>> waitables = new ArrayList<>();
|
||||
UrlMatcher matcher = UrlMatcher.forOneOf(options.glob, options.pattern, options.predicate);
|
||||
waitables.add(new WaitForNavigationHelper(matcher, options.waitUntil));
|
||||
UrlMatcher matcher = UrlMatcher.forOneOf(options.url);
|
||||
waitables.add(new WaitForNavigationHelper(matcher, convertViaJson(options.waitUntil, LoadState.class)));
|
||||
waitables.add(page.createWaitForCloseHelper());
|
||||
waitables.add(page.createWaitableFrameDetach(this));
|
||||
waitables.add(page.createWaitableNavigationTimeout(options.timeout));
|
||||
return runUntil(code, new WaitableRace<>(waitables));
|
||||
}
|
||||
|
||||
private static String toProtocol(WaitForSelectorOptions.State state) {
|
||||
return state.toString().toLowerCase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementHandle waitForSelector(String selector, WaitForSelectorOptions options) {
|
||||
return withLogging("Frame.waitForSelector", () -> waitForSelectorImpl(selector, options));
|
||||
@@ -903,10 +887,6 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("selector", selector);
|
||||
if (options.state != null) {
|
||||
params.remove("state");
|
||||
params.addProperty("state", toProtocol(options.state));
|
||||
}
|
||||
JsonElement json = sendMessage("waitForSelector", params);
|
||||
JsonObject element = json.getAsJsonObject().getAsJsonObject("element");
|
||||
if (element == null) {
|
||||
|
||||
@@ -25,7 +25,6 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.*;
|
||||
import static com.microsoft.playwright.impl.Utils.isFunctionBody;
|
||||
|
||||
public class JSHandleImpl extends ChannelOwner implements JSHandle {
|
||||
private String preview;
|
||||
@@ -51,7 +50,6 @@ public class JSHandleImpl extends ChannelOwner implements JSHandle {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.addProperty("world", "main");
|
||||
params.addProperty("isFunction", isFunctionBody(pageFunction));
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evaluateExpression", params);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
@@ -65,7 +63,6 @@ public class JSHandleImpl extends ChannelOwner implements JSHandle {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.addProperty("world", "main");
|
||||
params.addProperty("isFunction", isFunctionBody(pageFunction));
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evaluateExpressionHandle", params);
|
||||
return connection.getExistingObject(json.getAsJsonObject().getAsJsonObject("handle").get("guid").getAsString());
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
class LoggingSupport {
|
||||
@@ -28,12 +28,8 @@ class LoggingSupport {
|
||||
isEnabled = (debug != null) && debug.contains("pw:api");
|
||||
}
|
||||
|
||||
private static final SimpleDateFormat timestampFormat;
|
||||
static {
|
||||
timestampFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
|
||||
timestampFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
}
|
||||
|
||||
private static final DateTimeFormatter timestampFormat = DateTimeFormatter.ofPattern(
|
||||
"yyyy-MM-dd'T'HH:mm:ss.SSSXXX").withZone(ZoneId.of("UTC"));
|
||||
|
||||
void withLogging(String apiName, Runnable code) {
|
||||
withLogging(apiName, () -> {
|
||||
@@ -60,7 +56,7 @@ class LoggingSupport {
|
||||
|
||||
private void logApi(String message) {
|
||||
// This matches log format produced by the server.
|
||||
String timestamp = timestampFormat.format(new Date());
|
||||
String timestamp = ZonedDateTime.now().format(timestampFormat);
|
||||
System.err.println(timestamp + " pw:api " + message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,12 +16,10 @@
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Mouse;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
import static com.microsoft.playwright.impl.Serialization.toProtocol;
|
||||
import static com.microsoft.playwright.impl.Utils.convertViaJson;
|
||||
|
||||
class MouseImpl implements Mouse {
|
||||
@@ -43,10 +41,6 @@ class MouseImpl implements Mouse {
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("x", x);
|
||||
params.addProperty("y", y);
|
||||
if (options.button != null) {
|
||||
params.remove("button");
|
||||
params.addProperty("button", toProtocol(options.button));
|
||||
}
|
||||
page.sendMessage("mouseClick", params);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,16 +19,22 @@ package com.microsoft.playwright.impl;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.*;
|
||||
import com.microsoft.playwright.options.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.microsoft.playwright.options.ScreenshotType.JPEG;
|
||||
import static com.microsoft.playwright.options.ScreenshotType.PNG;
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
import static com.microsoft.playwright.impl.Utils.convertViaJson;
|
||||
import static com.microsoft.playwright.impl.Utils.isSafeCloseError;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.nio.file.Files.readAllBytes;
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
|
||||
@@ -37,9 +43,8 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
private final FrameImpl mainFrame;
|
||||
private final KeyboardImpl keyboard;
|
||||
private final MouseImpl mouse;
|
||||
private final AccessibilityImpl accessibility;
|
||||
private final TouchscreenImpl touchscreen;
|
||||
private Viewport viewport;
|
||||
private ViewportSize viewport;
|
||||
private final Router routes = new Router();
|
||||
private final Set<FrameImpl> frames = new LinkedHashSet<>();
|
||||
private final ListenerCollection<EventType> listeners = new ListenerCollection<EventType>() {
|
||||
@@ -59,7 +64,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
}
|
||||
};
|
||||
final Map<String, Binding> bindings = new HashMap<>();
|
||||
final Map<String, BindingCallback> bindings = new HashMap<>();
|
||||
BrowserContextImpl ownedContext;
|
||||
private boolean isClosed;
|
||||
final Set<Worker> workers = new HashSet<>();
|
||||
@@ -95,12 +100,11 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
mainFrame.page = this;
|
||||
isClosed = initializer.get("isClosed").getAsBoolean();
|
||||
if (initializer.has("viewportSize")) {
|
||||
viewport = gson().fromJson(initializer.get("viewportSize"), Viewport.class);
|
||||
viewport = gson().fromJson(initializer.get("viewportSize"), ViewportSize.class);
|
||||
}
|
||||
keyboard = new KeyboardImpl(this);
|
||||
mouse = new MouseImpl(this);
|
||||
touchscreen = new TouchscreenImpl(this);
|
||||
accessibility = new AccessibilityImpl(this);
|
||||
frames.add(mainFrame);
|
||||
timeoutSettings = new TimeoutSettings(browserContext.timeoutSettings);
|
||||
}
|
||||
@@ -110,10 +114,11 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
if ("dialog".equals(event)) {
|
||||
String guid = params.getAsJsonObject("dialog").get("guid").getAsString();
|
||||
DialogImpl dialog = connection.getExistingObject(guid);
|
||||
// If no action taken the dialog will stay open and execution will hang. We
|
||||
// could automatically dismiss the dialog if there are no listeners but we want
|
||||
// the behavior to match upstream one.
|
||||
listeners.notify(EventType.DIALOG, dialog);
|
||||
if (listeners.hasListeners(EventType.DIALOG)) {
|
||||
listeners.notify(EventType.DIALOG, dialog);
|
||||
} else {
|
||||
dialog.dismiss();
|
||||
}
|
||||
} else if ("popup".equals(event)) {
|
||||
String guid = params.getAsJsonObject("page").get("guid").getAsString();
|
||||
PageImpl popup = connection.getExistingObject(guid);
|
||||
@@ -144,7 +149,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
} else if ("bindingCall".equals(event)) {
|
||||
String guid = params.getAsJsonObject("binding").get("guid").getAsString();
|
||||
BindingCall bindingCall = connection.getExistingObject(guid);
|
||||
Binding binding = bindings.get(bindingCall.name());
|
||||
BindingCallback binding = bindings.get(bindingCall.name());
|
||||
if (binding == null) {
|
||||
binding = browserContext.bindings.get(bindingCall.name());
|
||||
}
|
||||
@@ -167,7 +172,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
String guid = params.getAsJsonObject("request").get("guid").getAsString();
|
||||
RequestImpl request = connection.getExistingObject(guid);
|
||||
if (params.has("failureText")) {
|
||||
request.failure = new Request.RequestFailure(params.get("failureText").getAsString());
|
||||
request.failure = params.get("failureText").getAsString();
|
||||
}
|
||||
listeners.notify(EventType.REQUESTFAILED, request);
|
||||
} else if ("requestFinished".equals(event)) {
|
||||
@@ -203,22 +208,33 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
handled = browserContext.routes.handle(route);
|
||||
}
|
||||
if (!handled) {
|
||||
route.continue_();
|
||||
route.resume();
|
||||
}
|
||||
} else if ("video".equals(event)) {
|
||||
video().setRelativePath(params.get("relativePath").getAsString());
|
||||
} else if ("pageError".equals(event)) {
|
||||
SerializedError error = gson().fromJson(params.getAsJsonObject("error"), SerializedError.class);
|
||||
listeners.notify(EventType.PAGEERROR, new ErrorImpl(error));
|
||||
String errorStr = "";
|
||||
if (error.error != null) {
|
||||
errorStr = error.error.name + ": " + error.error.message;
|
||||
if (error.error.stack != null && !error.error.stack.isEmpty()) {
|
||||
errorStr += "\n" + error.error.stack;
|
||||
}
|
||||
}
|
||||
listeners.notify(EventType.PAGEERROR, errorStr);
|
||||
} else if ("crash".equals(event)) {
|
||||
listeners.notify(EventType.CRASH, this);
|
||||
} else if ("close".equals(event)) {
|
||||
isClosed = true;
|
||||
browserContext.pages.remove(this);
|
||||
listeners.notify(EventType.CLOSE, this);
|
||||
didClose();
|
||||
}
|
||||
}
|
||||
|
||||
void didClose() {
|
||||
isClosed = true;
|
||||
browserContext.pages.remove(this);
|
||||
listeners.notify(EventType.CLOSE, this);
|
||||
}
|
||||
|
||||
private void willAddFileChooserListener() {
|
||||
if (!listeners.hasListeners(EventType.FILECHOOSER)) {
|
||||
updateFileChooserInterception(true);
|
||||
@@ -248,12 +264,12 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConsole(Consumer<ConsoleMessage> handler) {
|
||||
public void onConsoleMessage(Consumer<ConsoleMessage> handler) {
|
||||
listeners.add(EventType.CONSOLE, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void offConsole(Consumer<ConsoleMessage> handler) {
|
||||
public void offConsoleMessage(Consumer<ConsoleMessage> handler) {
|
||||
listeners.remove(EventType.CONSOLE, handler);
|
||||
}
|
||||
|
||||
@@ -348,12 +364,12 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageError(Consumer<Error> handler) {
|
||||
public void onPageError(Consumer<String> handler) {
|
||||
listeners.add(EventType.PAGEERROR, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void offPageError(Consumer<Error> handler) {
|
||||
public void offPageError(Consumer<String> handler) {
|
||||
listeners.remove(EventType.PAGEERROR, handler);
|
||||
}
|
||||
|
||||
@@ -428,7 +444,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page waitForClose(Runnable code, WaitForCloseOptions options) {
|
||||
public Page waitForClose(WaitForCloseOptions options, Runnable code) {
|
||||
if (options == null) {
|
||||
options = new WaitForCloseOptions();
|
||||
}
|
||||
@@ -436,9 +452,9 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConsoleMessage waitForConsole(Runnable code, WaitForConsoleOptions options) {
|
||||
public ConsoleMessage waitForConsoleMessage(WaitForConsoleMessageOptions options, Runnable code) {
|
||||
if (options == null) {
|
||||
options = new WaitForConsoleOptions();
|
||||
options = new WaitForConsoleMessageOptions();
|
||||
}
|
||||
return waitForEventWithTimeout(EventType.CONSOLE, code, options.timeout);
|
||||
}
|
||||
@@ -452,7 +468,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Download waitForDownload(Runnable code, WaitForDownloadOptions options) {
|
||||
public Download waitForDownload(WaitForDownloadOptions options, Runnable code) {
|
||||
if (options == null) {
|
||||
options = new WaitForDownloadOptions();
|
||||
}
|
||||
@@ -460,7 +476,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileChooser waitForFileChooser(Runnable code, WaitForFileChooserOptions options) {
|
||||
public FileChooser waitForFileChooser(WaitForFileChooserOptions options, Runnable code) {
|
||||
// TODO: enable/disable file chooser interception
|
||||
if (options == null) {
|
||||
options = new WaitForFileChooserOptions();
|
||||
@@ -469,39 +485,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Frame waitForFrameAttached(Runnable code, WaitForFrameAttachedOptions options) {
|
||||
if (options == null) {
|
||||
options = new WaitForFrameAttachedOptions();
|
||||
}
|
||||
return waitForEventWithTimeout(EventType.FRAMEATTACHED, code, options.timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Frame waitForFrameDetached(Runnable code, WaitForFrameDetachedOptions options) {
|
||||
if (options == null) {
|
||||
options = new WaitForFrameDetachedOptions();
|
||||
}
|
||||
return waitForEventWithTimeout(EventType.FRAMEDETACHED, code, options.timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Frame waitForFrameNavigated(Runnable code, WaitForFrameNavigatedOptions options) {
|
||||
if (options == null) {
|
||||
options = new WaitForFrameNavigatedOptions();
|
||||
}
|
||||
return waitForEventWithTimeout(EventType.FRAMENAVIGATED, code, options.timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Error waitForPageError(Runnable code, WaitForPageErrorOptions options) {
|
||||
if (options == null) {
|
||||
options = new WaitForPageErrorOptions();
|
||||
}
|
||||
return waitForEventWithTimeout(EventType.PAGEERROR, code, options.timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page waitForPopup(Runnable code, WaitForPopupOptions options) {
|
||||
public Page waitForPopup(WaitForPopupOptions options, Runnable code) {
|
||||
if (options == null) {
|
||||
options = new WaitForPopupOptions();
|
||||
}
|
||||
@@ -509,23 +493,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request waitForRequestFailed(Runnable code, WaitForRequestFailedOptions options) {
|
||||
if (options == null) {
|
||||
options = new WaitForRequestFailedOptions();
|
||||
}
|
||||
return waitForEventWithTimeout(EventType.REQUESTFAILED, code, options.timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request waitForRequestFinished(Runnable code, WaitForRequestFinishedOptions options) {
|
||||
if (options == null) {
|
||||
options = new WaitForRequestFinishedOptions();
|
||||
}
|
||||
return waitForEventWithTimeout(EventType.REQUESTFINISHED, code, options.timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebSocket waitForWebSocket(Runnable code, WaitForWebSocketOptions options) {
|
||||
public WebSocket waitForWebSocket(WaitForWebSocketOptions options, Runnable code) {
|
||||
if (options == null) {
|
||||
options = new WaitForWebSocketOptions();
|
||||
}
|
||||
@@ -533,7 +501,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Worker waitForWorker(Runnable code, WaitForWorkerOptions options) {
|
||||
public Worker waitForWorker(WaitForWorkerOptions options, Runnable code) {
|
||||
if (options == null) {
|
||||
options = new WaitForWorkerOptions();
|
||||
}
|
||||
@@ -579,20 +547,28 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Accessibility accessibility() {
|
||||
return accessibility;
|
||||
public void addInitScript(String script) {
|
||||
withLogging("Page.addInitScript", () -> addInitScriptImpl(script));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInitScript(String script, Object arg) {
|
||||
public void addInitScript(Path path) {
|
||||
withLogging("Page.addInitScript", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
// TODO: support or drop arg
|
||||
params.addProperty("source", script);
|
||||
sendMessage("addInitScript", params);
|
||||
try {
|
||||
byte[] bytes = readAllBytes(path);
|
||||
addInitScriptImpl(new String(bytes, UTF_8));
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to read script from file", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addInitScriptImpl(String script) {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("source", script);
|
||||
sendMessage("addInitScript", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementHandle addScriptTag(AddScriptTagOptions options) {
|
||||
return withLogging("Page.addScriptTag",
|
||||
@@ -645,13 +621,13 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emulateMedia(EmulateMediaParams options) {
|
||||
public void emulateMedia(EmulateMediaOptions options) {
|
||||
withLogging("Page.emulateMedia", () -> emulateMediaImpl(options));
|
||||
}
|
||||
|
||||
private void emulateMediaImpl(EmulateMediaParams options) {
|
||||
private void emulateMediaImpl(EmulateMediaOptions options) {
|
||||
if (options == null) {
|
||||
options = new EmulateMediaParams();
|
||||
options = new EmulateMediaOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
sendMessage("emulateMedia", params);
|
||||
@@ -668,11 +644,11 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exposeBinding(String name, Binding playwrightBinding, ExposeBindingOptions options) {
|
||||
public void exposeBinding(String name, BindingCallback playwrightBinding, ExposeBindingOptions options) {
|
||||
withLogging("Page.exposeBinding", () -> exposeBindingImpl(name, playwrightBinding, options));
|
||||
}
|
||||
|
||||
private void exposeBindingImpl(String name, Binding playwrightBinding, ExposeBindingOptions options) {
|
||||
private void exposeBindingImpl(String name, BindingCallback playwrightBinding, ExposeBindingOptions options) {
|
||||
if (bindings.containsKey(name)) {
|
||||
throw new PlaywrightException("Function \"" + name + "\" has been already registered");
|
||||
}
|
||||
@@ -690,9 +666,9 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exposeFunction(String name, Function playwrightFunction) {
|
||||
public void exposeFunction(String name, FunctionCallback playwrightFunction) {
|
||||
withLogging("Page.exposeFunction",
|
||||
() -> exposeBindingImpl(name, (Binding.Source source, Object... args) -> playwrightFunction.call(args), null));
|
||||
() -> exposeBindingImpl(name, (BindingCallback.Source source, Object... args) -> playwrightFunction.call(args), null));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -708,7 +684,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Frame frameByName(String name) {
|
||||
public Frame frame(String name) {
|
||||
for (Frame frame : frames) {
|
||||
if (name.equals(frame.name())) {
|
||||
return frame;
|
||||
@@ -762,8 +738,6 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
options = new GoBackOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.remove("waitUntil");
|
||||
params.addProperty("waitUntil", FrameImpl.toProtocol(options.waitUntil));
|
||||
JsonObject json = sendMessage("goBack", params).getAsJsonObject();
|
||||
if (json.has("response")) {
|
||||
return connection.getExistingObject(json.getAsJsonObject("response").get("guid").getAsString());
|
||||
@@ -781,8 +755,6 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
options = new GoForwardOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.remove("waitUntil");
|
||||
params.addProperty("waitUntil", FrameImpl.toProtocol(options.waitUntil));
|
||||
JsonObject json = sendMessage("goForward", params).getAsJsonObject();
|
||||
if (json.has("response")) {
|
||||
return connection.getExistingObject(json.getAsJsonObject("response").get("guid").getAsString());
|
||||
@@ -881,6 +853,13 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pause() {
|
||||
withLogging("BrowserContext.pause", () -> {
|
||||
context().pause();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] pdf(PdfOptions options) {
|
||||
return withLogging("Page.pdf", () -> pdfImpl(options));
|
||||
@@ -919,8 +898,6 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
options = new ReloadOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.remove("waitUntil");
|
||||
params.addProperty("waitUntil", FrameImpl.toProtocol(options.waitUntil));
|
||||
JsonObject json = sendMessage("reload", params).getAsJsonObject();
|
||||
if (json.has("response")) {
|
||||
return connection.getExistingObject(json.getAsJsonObject("response").get("guid").getAsString());
|
||||
@@ -954,35 +931,58 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
});
|
||||
}
|
||||
|
||||
private static String toProtocol(ScreenshotOptions.Type type) {
|
||||
return type.toString().toLowerCase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] screenshot(ScreenshotOptions options) {
|
||||
return withLogging("Page.screenshot", () -> screenshotImpl(options));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String selector, String value, SelectOptionOptions options) {
|
||||
String[] values = value == null ? null : new String[]{ value };
|
||||
return selectOption(selector, values, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String selector, ElementHandle value, SelectOptionOptions options) {
|
||||
ElementHandle[] values = value == null ? null : new ElementHandle[]{ value };
|
||||
return selectOption(selector, values, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String selector, String[] values, SelectOptionOptions options) {
|
||||
if (values == null) {
|
||||
return selectOption(selector, new SelectOption[0], options);
|
||||
}
|
||||
return selectOption(selector, Arrays.asList(values).stream().map(
|
||||
v -> new SelectOption().setValue(v)).toArray(SelectOption[]::new), options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String selector, SelectOption value, SelectOptionOptions options) {
|
||||
SelectOption[] values = value == null ? null : new SelectOption[]{value};
|
||||
return selectOption(selector, values, options);
|
||||
}
|
||||
|
||||
private byte[] screenshotImpl(ScreenshotOptions options) {
|
||||
if (options == null) {
|
||||
options = new ScreenshotOptions();
|
||||
}
|
||||
if (options.type == null) {
|
||||
options.type = ScreenshotOptions.Type.PNG;
|
||||
options.type = PNG;
|
||||
if (options.path != null) {
|
||||
String fileName = options.path.getFileName().toString();
|
||||
int extStart = fileName.lastIndexOf('.');
|
||||
if (extStart != -1) {
|
||||
String extension = fileName.substring(extStart).toLowerCase();
|
||||
if (".jpeg".equals(extension) || ".jpg".equals(extension)) {
|
||||
options.type = ScreenshotOptions.Type.JPEG;
|
||||
options.type = JPEG;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.remove("type");
|
||||
params.addProperty("type", toProtocol(options.type));
|
||||
params.remove("path");
|
||||
JsonObject json = sendMessage("screenshot", params).getAsJsonObject();
|
||||
|
||||
@@ -994,7 +994,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectOption(String selector, ElementHandle.SelectOption[] values, SelectOptionOptions options) {
|
||||
public List<String> selectOption(String selector, SelectOption[] values, SelectOptionOptions options) {
|
||||
return withLogging("Page.selectOption",
|
||||
() -> mainFrame.selectOptionImpl(selector, values, convertViaJson(options, Frame.SelectOptionOptions.class)));
|
||||
}
|
||||
@@ -1047,6 +1047,11 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(String selector, Path files, SetInputFilesOptions options) {
|
||||
setInputFiles(selector, new Path[]{files}, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(String selector, Path[] files, SetInputFilesOptions options) {
|
||||
withLogging("Page.setInputFiles",
|
||||
@@ -1054,7 +1059,12 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(String selector, FileChooser.FilePayload[] files, SetInputFilesOptions options) {
|
||||
public void setInputFiles(String selector, FilePayload files, SetInputFilesOptions options) {
|
||||
setInputFiles(selector, new FilePayload[]{files}, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputFiles(String selector, FilePayload[] files, SetInputFilesOptions options) {
|
||||
withLogging("Page.setInputFiles",
|
||||
() -> mainFrame.setInputFilesImpl(selector, files, convertViaJson(options, Frame.SetInputFilesOptions.class)));
|
||||
}
|
||||
@@ -1062,7 +1072,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
@Override
|
||||
public void setViewportSize(int width, int height) {
|
||||
withLogging("Page.setViewportSize", () -> {
|
||||
viewport = new Viewport(width, height);
|
||||
viewport = new ViewportSize(width, height);
|
||||
JsonObject params = new JsonObject();
|
||||
params.add("viewportSize", gson().toJsonTree(viewport));
|
||||
sendMessage("setViewportSize", params);
|
||||
@@ -1151,7 +1161,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Viewport viewportSize() {
|
||||
public ViewportSize viewportSize() {
|
||||
return viewport;
|
||||
}
|
||||
|
||||
@@ -1172,11 +1182,11 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
@Override
|
||||
public void waitForLoadState(LoadState state, WaitForLoadStateOptions options) {
|
||||
withLogging("Page.waitForLoadState",
|
||||
() -> mainFrame.waitForLoadStateImpl(convertViaJson(state, Frame.LoadState.class), convertViaJson(options, Frame.WaitForLoadStateOptions.class)));
|
||||
() -> mainFrame.waitForLoadStateImpl(state, convertViaJson(options, Frame.WaitForLoadStateOptions.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response waitForNavigation(Runnable code, WaitForNavigationOptions options) {
|
||||
public Response waitForNavigation(WaitForNavigationOptions options, Runnable code) {
|
||||
return withLogging("Page.waitForNavigation", () -> waitForNavigationImpl(code, options));
|
||||
}
|
||||
|
||||
@@ -1185,9 +1195,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
if (options != null) {
|
||||
frameOptions.timeout = options.timeout;
|
||||
frameOptions.waitUntil = options.waitUntil;
|
||||
frameOptions.glob = options.glob;
|
||||
frameOptions.pattern = options.pattern;
|
||||
frameOptions.predicate = options.predicate;
|
||||
frameOptions.url = options.url;
|
||||
}
|
||||
return mainFrame.waitForNavigationImpl(code, frameOptions);
|
||||
}
|
||||
@@ -1196,29 +1204,6 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
listeners.notify(EventType.FRAMENAVIGATED, frame);
|
||||
}
|
||||
|
||||
private static class ErrorImpl implements Error {
|
||||
private final SerializedError error;
|
||||
|
||||
ErrorImpl(SerializedError error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String message() {
|
||||
return error.error.message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return error.error.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String stack() {
|
||||
return error.error.stack;
|
||||
}
|
||||
}
|
||||
|
||||
private class WaitableFrameDetach extends WaitableEvent<EventType, Frame> {
|
||||
WaitableFrameDetach(Frame frameArg) {
|
||||
super(PageImpl.this.listeners, EventType.FRAMEDETACHED, detachedFrame -> frameArg.equals(detachedFrame));
|
||||
@@ -1263,25 +1248,25 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request waitForRequest(Runnable code, String urlGlob, WaitForRequestOptions options) {
|
||||
return waitForRequest(code, toRequestPredicate(new UrlMatcher(urlGlob)), options);
|
||||
public Request waitForRequest(String urlGlob, WaitForRequestOptions options, Runnable code) {
|
||||
return waitForRequest(toRequestPredicate(new UrlMatcher(urlGlob)), options, code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request waitForRequest(Runnable code, Pattern urlPattern, WaitForRequestOptions options) {
|
||||
return waitForRequest(code, toRequestPredicate(new UrlMatcher(urlPattern)), options);
|
||||
public Request waitForRequest(Pattern urlPattern, WaitForRequestOptions options, Runnable code) {
|
||||
return waitForRequest(toRequestPredicate(new UrlMatcher(urlPattern)), options, code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request waitForRequest(Runnable code, Predicate<Request> predicate, WaitForRequestOptions options) {
|
||||
return withLogging("Page.waitForRequest", () -> waitForRequestImpl(code, predicate, options));
|
||||
public Request waitForRequest(Predicate<Request> predicate, WaitForRequestOptions options, Runnable code) {
|
||||
return withLogging("Page.waitForRequest", () -> waitForRequestImpl(predicate, options, code));
|
||||
}
|
||||
|
||||
private static Predicate<Request> toRequestPredicate(UrlMatcher matcher) {
|
||||
return request -> matcher.test(request.url());
|
||||
}
|
||||
|
||||
private Request waitForRequestImpl(Runnable code, Predicate<Request> predicate, WaitForRequestOptions options) {
|
||||
private Request waitForRequestImpl(Predicate<Request> predicate, WaitForRequestOptions options, Runnable code) {
|
||||
if (options == null) {
|
||||
options = new WaitForRequestOptions();
|
||||
}
|
||||
@@ -1294,25 +1279,25 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response waitForResponse(Runnable code, String urlGlob, WaitForResponseOptions options) {
|
||||
return waitForResponse(code, toResponsePredicate(new UrlMatcher(urlGlob)), options);
|
||||
public Response waitForResponse(String urlGlob, WaitForResponseOptions options, Runnable code) {
|
||||
return waitForResponse(toResponsePredicate(new UrlMatcher(urlGlob)), options, code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response waitForResponse(Runnable code, Pattern urlPattern, WaitForResponseOptions options) {
|
||||
return waitForResponse(code, toResponsePredicate(new UrlMatcher(urlPattern)), options);
|
||||
public Response waitForResponse(Pattern urlPattern, WaitForResponseOptions options, Runnable code) {
|
||||
return waitForResponse(toResponsePredicate(new UrlMatcher(urlPattern)), options, code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response waitForResponse(Runnable code, Predicate<Response> predicate, WaitForResponseOptions options) {
|
||||
return withLogging("Page.waitForResponse", () -> waitForResponseImpl(code, predicate, options));
|
||||
public Response waitForResponse(Predicate<Response> predicate, WaitForResponseOptions options, Runnable code) {
|
||||
return withLogging("Page.waitForResponse", () -> waitForResponseImpl(predicate, options, code));
|
||||
}
|
||||
|
||||
private static Predicate<Response> toResponsePredicate(UrlMatcher matcher) {
|
||||
return response -> matcher.test(response.url());
|
||||
}
|
||||
|
||||
private Response waitForResponseImpl(Runnable code, Predicate<Response> predicate, WaitForResponseOptions options) {
|
||||
private Response waitForResponseImpl(Predicate<Response> predicate, WaitForResponseOptions options, Runnable code) {
|
||||
if (options == null) {
|
||||
options = new WaitForResponseOptions();
|
||||
}
|
||||
@@ -1339,4 +1324,18 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
public List<Worker> workers() {
|
||||
return new ArrayList<>(workers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onceDialog(Consumer<Dialog> handler) {
|
||||
onDialog(new Consumer<Dialog>() {
|
||||
@Override
|
||||
public void accept(Dialog dialog) {
|
||||
try {
|
||||
handler.accept(dialog);
|
||||
} finally {
|
||||
offDialog(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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.PlaywrightException;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class PipeTransport implements Transport {
|
||||
private final BlockingQueue<String> incoming = new ArrayBlockingQueue<>(1000);
|
||||
private final BlockingQueue<String> outgoing= new ArrayBlockingQueue<>(1000);
|
||||
|
||||
private final ReaderThread readerThread;
|
||||
private final WriterThread writerThread;
|
||||
|
||||
private boolean isClosed;
|
||||
|
||||
PipeTransport(InputStream input, OutputStream output) {
|
||||
DataInputStream in = new DataInputStream(new BufferedInputStream(input));
|
||||
readerThread = new ReaderThread(in, incoming);
|
||||
readerThread.start();
|
||||
writerThread = new WriterThread(output, outgoing);
|
||||
writerThread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(String message) {
|
||||
if (isClosed) {
|
||||
throw new PlaywrightException("Playwright connection closed");
|
||||
}
|
||||
try {
|
||||
outgoing.put(message);
|
||||
} catch (InterruptedException e) {
|
||||
throw new PlaywrightException("Failed to send message", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String poll(Duration timeout) {
|
||||
if (isClosed) {
|
||||
throw new PlaywrightException("Playwright connection closed");
|
||||
}
|
||||
try {
|
||||
return incoming.poll(timeout.toMillis(), TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
throw new PlaywrightException("Failed to read message", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
isClosed = true;
|
||||
// We interrupt only the outgoing pipe and keep reader thread running as
|
||||
// otherwise child process may block on writing to its stdout and never
|
||||
// exit (observed on Windows).
|
||||
readerThread.isClosing = true;
|
||||
writerThread.out.close();
|
||||
writerThread.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
class ReaderThread extends Thread {
|
||||
private final DataInputStream in;
|
||||
private final BlockingQueue<String> queue;
|
||||
volatile boolean isClosing;
|
||||
|
||||
private static int readIntLE(DataInputStream in) throws IOException {
|
||||
int ch1 = in.read();
|
||||
int ch2 = in.read();
|
||||
int ch3 = in.read();
|
||||
int ch4 = in.read();
|
||||
if ((ch1 | ch2 | ch3 | ch4) < 0) {
|
||||
throw new EOFException();
|
||||
} else {
|
||||
return (ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0);
|
||||
}
|
||||
}
|
||||
|
||||
ReaderThread(DataInputStream in, BlockingQueue<String> queue) {
|
||||
this.in = in;
|
||||
this.queue = queue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (!isInterrupted()) {
|
||||
try {
|
||||
queue.put(readMessage());
|
||||
} catch (IOException e) {
|
||||
if (!isInterrupted() && !isClosing) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
break;
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String readMessage() throws IOException {
|
||||
int len = readIntLE(in);
|
||||
byte[] raw = new byte[len];
|
||||
in.readFully(raw, 0, len);
|
||||
return new String(raw, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
class WriterThread extends Thread {
|
||||
final OutputStream out;
|
||||
private final BlockingQueue<String> queue;
|
||||
|
||||
private static void writeIntLE(OutputStream out, int v) throws IOException {
|
||||
out.write(v >>> 0 & 255);
|
||||
out.write(v >>> 8 & 255);
|
||||
out.write(v >>> 16 & 255);
|
||||
out.write(v >>> 24 & 255);
|
||||
}
|
||||
|
||||
WriterThread(OutputStream out, BlockingQueue<String> queue) {
|
||||
this.out = out;
|
||||
this.queue = queue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (!isInterrupted()) {
|
||||
try {
|
||||
if (queue.isEmpty())
|
||||
out.flush();
|
||||
sendMessage(queue.take());
|
||||
} catch (IOException e) {
|
||||
if (!isInterrupted())
|
||||
e.printStackTrace();
|
||||
break;
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendMessage(String message) throws IOException {
|
||||
byte[] bytes = message.getBytes(StandardCharsets.UTF_8);
|
||||
writeIntLE(out, bytes.length);
|
||||
out.write(bytes);
|
||||
}
|
||||
}
|
||||
@@ -16,20 +16,13 @@
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.DeviceDescriptor;
|
||||
import com.microsoft.playwright.Playwright;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
import com.microsoft.playwright.Selectors;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class PlaywrightImpl extends ChannelOwner implements Playwright {
|
||||
@@ -42,7 +35,7 @@ public class PlaywrightImpl extends ChannelOwner implements Playwright {
|
||||
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
|
||||
// pb.environment().put("DEBUG", "pw:pro*");
|
||||
Process p = pb.start();
|
||||
Connection connection = new Connection(p.getInputStream(), p.getOutputStream());
|
||||
Connection connection = new Connection(new PipeTransport(p.getInputStream(), p.getOutputStream()));
|
||||
PlaywrightImpl result = (PlaywrightImpl) connection.waitForObjectWithKnownName("Playwright");
|
||||
result.driverProcess = p;
|
||||
return result;
|
||||
@@ -54,24 +47,15 @@ public class PlaywrightImpl extends ChannelOwner implements Playwright {
|
||||
private final BrowserTypeImpl chromium;
|
||||
private final BrowserTypeImpl firefox;
|
||||
private final BrowserTypeImpl webkit;
|
||||
private final Selectors selectors;
|
||||
private final Map<String, DeviceDescriptor> devices = new HashMap<>();
|
||||
final SharedSelectors sharedSelectors = new SharedSelectors();;
|
||||
|
||||
PlaywrightImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
chromium = parent.connection.getExistingObject(initializer.getAsJsonObject("chromium").get("guid").getAsString());
|
||||
firefox = parent.connection.getExistingObject(initializer.getAsJsonObject("firefox").get("guid").getAsString());
|
||||
webkit = parent.connection.getExistingObject(initializer.getAsJsonObject("webkit").get("guid").getAsString());
|
||||
selectors = parent.connection.getExistingObject(initializer.getAsJsonObject("selectors").get("guid").getAsString());
|
||||
|
||||
Gson gson = Serialization.gson();
|
||||
for (JsonElement item : initializer.getAsJsonArray("deviceDescriptors")) {
|
||||
JsonObject o = item.getAsJsonObject();
|
||||
String name = o.get("name").getAsString();
|
||||
DeviceDescriptorImpl descriptor = gson.fromJson(o.get("descriptor"), DeviceDescriptorImpl.class);
|
||||
descriptor.playwright = this;
|
||||
devices.put(name, descriptor);
|
||||
}
|
||||
SelectorsImpl channel = parent.connection.getExistingObject(initializer.getAsJsonObject("selectors").get("guid").getAsString());
|
||||
sharedSelectors.addChannel(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -89,23 +73,25 @@ public class PlaywrightImpl extends ChannelOwner implements Playwright {
|
||||
return webkit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, DeviceDescriptor> devices() {
|
||||
return devices;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Selectors selectors() {
|
||||
return selectors;
|
||||
return sharedSelectors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
connection.close();
|
||||
// playwright-cli will exit when its stdin is closed, we wait for that.
|
||||
boolean didClose = driverProcess.waitFor(30, TimeUnit.SECONDS);
|
||||
if (!didClose) {
|
||||
System.err.println("WARNING: Timed out while waiting for driver process to exit");
|
||||
public void close() {
|
||||
try {
|
||||
connection.close();
|
||||
// playwright-cli will exit when its stdin is closed, we wait for that.
|
||||
boolean didClose = driverProcess.waitFor(30, TimeUnit.SECONDS);
|
||||
if (!didClose) {
|
||||
System.err.println("WARNING: Timed out while waiting for driver process to exit");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to terminate", e);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new PlaywrightException("Operation interrupted", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.google.gson.JsonObject;
|
||||
|
||||
public class RemoteBrowser extends ChannelOwner {
|
||||
RemoteBrowser(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
}
|
||||
|
||||
BrowserImpl browser() {
|
||||
return connection.getExistingObject(initializer.getAsJsonObject("browser").get("guid").getAsString());
|
||||
}
|
||||
|
||||
SelectorsImpl selectors() {
|
||||
return connection.getExistingObject(initializer.getAsJsonObject("selectors").get("guid").getAsString());
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Frame;
|
||||
import com.microsoft.playwright.Request;
|
||||
import com.microsoft.playwright.Response;
|
||||
import com.microsoft.playwright.options.Timing;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
@@ -32,8 +33,8 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
private RequestImpl redirectedFrom;
|
||||
private RequestImpl redirectedTo;
|
||||
final Map<String, String> headers = new HashMap<>();
|
||||
RequestFailure failure;
|
||||
RequestTiming timing;
|
||||
String failure;
|
||||
Timing timing;
|
||||
|
||||
RequestImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
@@ -54,7 +55,7 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestFailure failure() {
|
||||
public String failure() {
|
||||
return failure;
|
||||
}
|
||||
|
||||
@@ -118,8 +119,8 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestTiming timing() {
|
||||
return null;
|
||||
public Timing timing() {
|
||||
return timing;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Frame;
|
||||
import com.microsoft.playwright.Request;
|
||||
import com.microsoft.playwright.Response;
|
||||
import com.microsoft.playwright.options.Timing;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
@@ -45,7 +46,7 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
||||
JsonObject item = e.getAsJsonObject();
|
||||
request.headers.put(item.get("name").getAsString().toLowerCase(), item.get("value").getAsString());
|
||||
}
|
||||
request.timing = Serialization.gson().fromJson(initializer.get("timing"), Request.RequestTiming.class);
|
||||
request.timing = Serialization.gson().fromJson(initializer.get("timing"), Timing.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,16 +16,15 @@
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
import com.microsoft.playwright.Request;
|
||||
import com.microsoft.playwright.Route;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -44,13 +43,13 @@ public class RouteImpl extends ChannelOwner implements Route {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void continue_(ContinueOptions options) {
|
||||
withLogging("Route.continue", () -> continueImpl(options));
|
||||
public void resume(ResumeOptions options) {
|
||||
withLogging("Route.resume", () -> resumeImpl(options));
|
||||
}
|
||||
|
||||
private void continueImpl(ContinueOptions options) {
|
||||
private void resumeImpl(ResumeOptions options) {
|
||||
if (options == null) {
|
||||
options = new ContinueOptions();
|
||||
options = new ResumeOptions();
|
||||
}
|
||||
JsonObject params = new JsonObject();
|
||||
if (options.url != null) {
|
||||
@@ -63,7 +62,15 @@ public class RouteImpl extends ChannelOwner implements Route {
|
||||
params.add("headers", Serialization.toProtocol(options.headers));
|
||||
}
|
||||
if (options.postData != null) {
|
||||
String base64 = Base64.getEncoder().encodeToString(options.postData);
|
||||
byte[] bytes = null;
|
||||
if (options.postData instanceof byte[]) {
|
||||
bytes = (byte[]) options.postData;
|
||||
} else if (options.postData instanceof String) {
|
||||
bytes = ((String) options.postData).getBytes(StandardCharsets.UTF_8);
|
||||
} else {
|
||||
throw new PlaywrightException("postData must be either String or byte[], found: " + options.postData.getClass().getName());
|
||||
}
|
||||
String base64 = Base64.getEncoder().encodeToString(bytes);
|
||||
params.addProperty("postData", base64);
|
||||
}
|
||||
sendMessage("continue", params);
|
||||
|
||||
@@ -16,12 +16,10 @@
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.microsoft.playwright.Request;
|
||||
import com.microsoft.playwright.Route;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
@@ -27,34 +27,18 @@ import java.nio.file.Path;
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
class SelectorsImpl extends ChannelOwner implements Selectors {
|
||||
class SelectorsImpl extends ChannelOwner {
|
||||
SelectorsImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(String name, String script, RegisterOptions options) {
|
||||
withLogging("Selectors.register", () -> registerImpl(name, script, options));
|
||||
}
|
||||
|
||||
private void registerImpl(String name, String script, RegisterOptions options) {
|
||||
void registerImpl(String name, String script, Selectors.RegisterOptions options) {
|
||||
if (options == null) {
|
||||
options = new RegisterOptions();
|
||||
options = new Selectors.RegisterOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
params.addProperty("name", name);
|
||||
params.addProperty("source", script);
|
||||
sendMessage("register", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(String name, Path path, RegisterOptions options) {
|
||||
byte[] buffer;
|
||||
try {
|
||||
buffer = Files.readAllBytes(path);
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to read selector from file: " + path, e);
|
||||
}
|
||||
register(name, new String(buffer, UTF_8), options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.google.gson.*;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import com.microsoft.playwright.options.*;
|
||||
import com.microsoft.playwright.*;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -36,9 +37,15 @@ class Serialization {
|
||||
static Gson gson() {
|
||||
if (gson == null) {
|
||||
gson = new GsonBuilder()
|
||||
.registerTypeAdapter(BrowserContext.SameSite.class, new SameSiteAdapter().nullSafe())
|
||||
.registerTypeAdapter(SameSiteAttribute.class, new SameSiteAdapter().nullSafe())
|
||||
.registerTypeAdapter(ColorScheme.class, new ColorSchemeAdapter().nullSafe())
|
||||
.registerTypeAdapter(Page.EmulateMediaParams.Media.class, new MediaSerializer())
|
||||
.registerTypeAdapter(Media.class, new MediaSerializer())
|
||||
.registerTypeAdapter(ScreenshotType.class, new ToLowerCaseSerializer<ScreenshotType>())
|
||||
.registerTypeAdapter(MouseButton.class, new ToLowerCaseSerializer<MouseButton>())
|
||||
.registerTypeAdapter(LoadState.class, new ToLowerCaseSerializer<LoadState>())
|
||||
.registerTypeAdapter(WaitUntilState.class, new ToLowerCaseSerializer<WaitUntilState>())
|
||||
.registerTypeAdapter(WaitForSelectorState.class, new ToLowerCaseSerializer<WaitForSelectorState>())
|
||||
.registerTypeAdapter((new TypeToken<List<KeyboardModifier>>(){}).getType(), new KeyboardModifiersSerializer())
|
||||
.registerTypeAdapter(Optional.class, new OptionalSerializer())
|
||||
.registerTypeHierarchyAdapter(JSHandleImpl.class, new HandleSerializer())
|
||||
.registerTypeHierarchyAdapter(Map.class, new StringMapSerializer())
|
||||
@@ -180,39 +187,29 @@ class Serialization {
|
||||
throw new PlaywrightException("Unexpected result: " + gson().toJson(value));
|
||||
}
|
||||
|
||||
static String toProtocol(Mouse.Button button) {
|
||||
switch (button) {
|
||||
case LEFT:
|
||||
return "left";
|
||||
case RIGHT:
|
||||
return "right";
|
||||
case MIDDLE:
|
||||
return "middle";
|
||||
default:
|
||||
throw new PlaywrightException("Unexpected value: " + button);
|
||||
private static class KeyboardModifiersSerializer implements JsonSerializer<List<KeyboardModifier>> {
|
||||
@Override
|
||||
public JsonArray serialize(List<KeyboardModifier> modifiers, Type typeOfSrc, JsonSerializationContext context) {
|
||||
JsonArray result = new JsonArray();
|
||||
if (modifiers.contains(KeyboardModifier.ALT)) {
|
||||
result.add("Alt");
|
||||
}
|
||||
if (modifiers.contains(KeyboardModifier.CONTROL)) {
|
||||
result.add("Control");
|
||||
}
|
||||
if (modifiers.contains(KeyboardModifier.META)) {
|
||||
result.add("Meta");
|
||||
}
|
||||
if (modifiers.contains(KeyboardModifier.SHIFT)) {
|
||||
result.add("Shift");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
static JsonArray toProtocol(Set<Keyboard.Modifier> modifiers) {
|
||||
JsonArray result = new JsonArray();
|
||||
if (modifiers.contains(Keyboard.Modifier.ALT)) {
|
||||
result.add("Alt");
|
||||
}
|
||||
if (modifiers.contains(Keyboard.Modifier.CONTROL)) {
|
||||
result.add("Control");
|
||||
}
|
||||
if (modifiers.contains(Keyboard.Modifier.META)) {
|
||||
result.add("Meta");
|
||||
}
|
||||
if (modifiers.contains(Keyboard.Modifier.SHIFT)) {
|
||||
result.add("Shift");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static JsonArray toJsonArray(FileChooser.FilePayload[] files) {
|
||||
static JsonArray toJsonArray(FilePayload[] files) {
|
||||
JsonArray jsonFiles = new JsonArray();
|
||||
for (FileChooser.FilePayload p : files) {
|
||||
for (FilePayload p : files) {
|
||||
JsonObject jsonFile = new JsonObject();
|
||||
jsonFile.addProperty("name", p.name);
|
||||
jsonFile.addProperty("mimeType", p.mimeType);
|
||||
@@ -253,13 +250,14 @@ class Serialization {
|
||||
|
||||
private static class OptionalSerializer implements JsonSerializer<Optional<?>> {
|
||||
private static boolean isSupported(Type type) {
|
||||
return new TypeToken<Optional<Page.EmulateMediaParams.Media>>() {}.getType().getTypeName().equals(type.getTypeName()) ||
|
||||
new TypeToken<Optional<ColorScheme>>() {}.getType().getTypeName().equals(type.getTypeName());
|
||||
return new TypeToken<Optional<Media>>() {}.getType().getTypeName().equals(type.getTypeName()) ||
|
||||
new TypeToken<Optional<ColorScheme>>() {}.getType().getTypeName().equals(type.getTypeName()) ||
|
||||
new TypeToken<Optional<ViewportSize>>() {}.getType().getTypeName().equals(type.getTypeName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement serialize(Optional<?> src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
assert isSupported(typeOfSrc);
|
||||
assert isSupported(typeOfSrc) : "Unexpected optional type: " + typeOfSrc.getTypeName();
|
||||
if (!src.isPresent()) {
|
||||
return new JsonPrimitive("null");
|
||||
}
|
||||
@@ -286,9 +284,9 @@ class Serialization {
|
||||
}
|
||||
}
|
||||
|
||||
private static class MediaSerializer implements JsonSerializer<Page.EmulateMediaParams.Media> {
|
||||
private static class MediaSerializer implements JsonSerializer<Media> {
|
||||
@Override
|
||||
public JsonElement serialize(Page.EmulateMediaParams.Media src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
public JsonElement serialize(Media src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src.toString().toLowerCase());
|
||||
}
|
||||
}
|
||||
@@ -300,9 +298,16 @@ class Serialization {
|
||||
}
|
||||
}
|
||||
|
||||
private static class SameSiteAdapter extends TypeAdapter<BrowserContext.SameSite> {
|
||||
private static class ToLowerCaseSerializer<E extends Enum<E>> implements JsonSerializer<E> {
|
||||
@Override
|
||||
public void write(JsonWriter out, BrowserContext.SameSite value) throws IOException {
|
||||
public JsonElement serialize(E src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src.toString().toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
private static class SameSiteAdapter extends TypeAdapter<SameSiteAttribute> {
|
||||
@Override
|
||||
public void write(JsonWriter out, SameSiteAttribute value) throws IOException {
|
||||
String stringValue;
|
||||
switch (value) {
|
||||
case STRICT:
|
||||
@@ -321,9 +326,9 @@ class Serialization {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BrowserContext.SameSite read(JsonReader in) throws IOException {
|
||||
public SameSiteAttribute read(JsonReader in) throws IOException {
|
||||
String value = in.nextString();
|
||||
return BrowserContext.SameSite.valueOf(value.toUpperCase());
|
||||
return SameSiteAttribute.valueOf(value.toUpperCase());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* 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.PlaywrightException;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
class ServerException extends PlaywrightException {
|
||||
private final SerializedError.Error error;
|
||||
|
||||
ServerException(SerializedError.Error error) {
|
||||
super(error.name + ": " + error.message);
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printStackTrace(PrintWriter s) {
|
||||
super.printStackTrace(s);
|
||||
s.println("Caused by Playwright server error:");
|
||||
s.println(getMessage());
|
||||
s.println(error.stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printStackTrace(PrintStream s) {
|
||||
super.printStackTrace(s);
|
||||
s.println("Caused by Playwright server error:");
|
||||
s.println(getMessage());
|
||||
s.println(error.stack);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.PlaywrightException;
|
||||
import com.microsoft.playwright.Selectors;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
public class SharedSelectors extends LoggingSupport implements Selectors {
|
||||
private final List<SelectorsImpl> channels = new ArrayList<>();
|
||||
private final List<Registration> registrations = new ArrayList<>();
|
||||
|
||||
private static class Registration {
|
||||
final String name;
|
||||
final String script;
|
||||
final RegisterOptions options;
|
||||
|
||||
Registration(String name, String script, RegisterOptions options) {
|
||||
this.name = name;
|
||||
this.script = script;
|
||||
this.options = options;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(String name, String script, RegisterOptions options) {
|
||||
withLogging("Selectors.register", () -> registerImpl(name, script, options));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(String name, Path path, RegisterOptions options) {
|
||||
withLogging("Selectors.register", () -> {
|
||||
byte[] buffer;
|
||||
try {
|
||||
buffer = Files.readAllBytes(path);
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to read selector from file: " + path, e);
|
||||
}
|
||||
registerImpl(name, new String(buffer, UTF_8), options);
|
||||
});
|
||||
}
|
||||
|
||||
void addChannel(SelectorsImpl channel) {
|
||||
registrations.forEach(r -> channel.registerImpl(r.name, r.script, r.options));
|
||||
channels.add(channel);
|
||||
}
|
||||
|
||||
void removeChannel(SelectorsImpl channel) {
|
||||
channels.remove(channel);
|
||||
}
|
||||
|
||||
private void registerImpl(String name, String script, RegisterOptions options) {
|
||||
channels.forEach(impl -> impl.registerImpl(name, script, options));
|
||||
registrations.add(new Registration(name, script, options));
|
||||
}
|
||||
}
|
||||
@@ -13,152 +13,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class Transport {
|
||||
private final BlockingQueue<String> incoming = new ArrayBlockingQueue<>(1000);
|
||||
private final BlockingQueue<String> outgoing= new ArrayBlockingQueue<>(1000);
|
||||
|
||||
private final ReaderThread readerThread;
|
||||
private final WriterThread writerThread;
|
||||
|
||||
private boolean isClosed;
|
||||
|
||||
Transport(InputStream input, OutputStream output) {
|
||||
DataInputStream in = new DataInputStream(new BufferedInputStream(input));
|
||||
readerThread = new ReaderThread(in, incoming);
|
||||
readerThread.start();
|
||||
writerThread = new WriterThread(output, outgoing);
|
||||
writerThread.start();
|
||||
}
|
||||
|
||||
public void send(String message) {
|
||||
if (isClosed) {
|
||||
throw new PlaywrightException("Playwright connection closed");
|
||||
}
|
||||
try {
|
||||
outgoing.put(message);
|
||||
} catch (InterruptedException e) {
|
||||
throw new PlaywrightException("Failed to send message", e);
|
||||
}
|
||||
}
|
||||
|
||||
public String poll(Duration timeout) {
|
||||
if (isClosed) {
|
||||
throw new PlaywrightException("Playwright connection closed");
|
||||
}
|
||||
try {
|
||||
return incoming.poll(timeout.toMillis(), TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
throw new PlaywrightException("Failed to read message", e);
|
||||
}
|
||||
}
|
||||
|
||||
void close() throws IOException {
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
isClosed = true;
|
||||
// We interrupt only the outgoing pipe and keep reader thread running as
|
||||
// otherwise child process may block on writing to its stdout and never
|
||||
// exit (observed on Windows).
|
||||
readerThread.isClosing = true;
|
||||
writerThread.out.close();
|
||||
writerThread.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
class ReaderThread extends Thread {
|
||||
private final DataInputStream in;
|
||||
private final BlockingQueue<String> queue;
|
||||
volatile boolean isClosing;
|
||||
|
||||
private static int readIntLE(DataInputStream in) throws IOException {
|
||||
int ch1 = in.read();
|
||||
int ch2 = in.read();
|
||||
int ch3 = in.read();
|
||||
int ch4 = in.read();
|
||||
if ((ch1 | ch2 | ch3 | ch4) < 0) {
|
||||
throw new EOFException();
|
||||
} else {
|
||||
return (ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0);
|
||||
}
|
||||
}
|
||||
|
||||
ReaderThread(DataInputStream in, BlockingQueue<String> queue) {
|
||||
this.in = in;
|
||||
this.queue = queue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (!isInterrupted()) {
|
||||
try {
|
||||
queue.put(readMessage());
|
||||
} catch (IOException e) {
|
||||
if (!isInterrupted() && !isClosing) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
break;
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String readMessage() throws IOException {
|
||||
int len = readIntLE(in);
|
||||
byte[] raw = new byte[len];
|
||||
in.readFully(raw, 0, len);
|
||||
return new String(raw, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
class WriterThread extends Thread {
|
||||
final OutputStream out;
|
||||
private final BlockingQueue<String> queue;
|
||||
|
||||
private static void writeIntLE(OutputStream out, int v) throws IOException {
|
||||
out.write(v >>> 0 & 255);
|
||||
out.write(v >>> 8 & 255);
|
||||
out.write(v >>> 16 & 255);
|
||||
out.write(v >>> 24 & 255);
|
||||
}
|
||||
|
||||
WriterThread(OutputStream out, BlockingQueue<String> queue) {
|
||||
this.out = out;
|
||||
this.queue = queue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (!isInterrupted()) {
|
||||
try {
|
||||
if (queue.isEmpty())
|
||||
out.flush();
|
||||
sendMessage(queue.take());
|
||||
} catch (IOException e) {
|
||||
if (!isInterrupted())
|
||||
e.printStackTrace();
|
||||
break;
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendMessage(String message) throws IOException {
|
||||
byte[] bytes = message.getBytes(StandardCharsets.UTF_8);
|
||||
writeIntLE(out, bytes.length);
|
||||
out.write(bytes);
|
||||
}
|
||||
public interface Transport {
|
||||
void send(String message);
|
||||
String poll(Duration timeout);
|
||||
void close() throws IOException;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -34,25 +36,20 @@ class UrlMatcher {
|
||||
return new UrlMatcher(null, null);
|
||||
}
|
||||
|
||||
static UrlMatcher forOneOf(String glob, Pattern pattern, Predicate<String> predicate) {
|
||||
UrlMatcher result = UrlMatcher.any();
|
||||
int conditionCount = 0;
|
||||
if (glob != null) {
|
||||
conditionCount += 1;
|
||||
result = new UrlMatcher(glob);
|
||||
static UrlMatcher forOneOf(Object object) {
|
||||
if (object == null) {
|
||||
return UrlMatcher.any();
|
||||
}
|
||||
if (pattern != null) {
|
||||
conditionCount += 1;
|
||||
result = new UrlMatcher(pattern);
|
||||
if (object instanceof String) {
|
||||
return new UrlMatcher((String) object);
|
||||
}
|
||||
if (predicate != null) {
|
||||
conditionCount += 1;
|
||||
result = new UrlMatcher(predicate);
|
||||
if (object instanceof Pattern) {
|
||||
return new UrlMatcher((Pattern) object);
|
||||
}
|
||||
if (conditionCount > 1) {
|
||||
throw new IllegalArgumentException("Only one of glob, pattern and predicate can be specified");
|
||||
if (object instanceof Predicate) {
|
||||
return new UrlMatcher((Predicate<String>) object);
|
||||
}
|
||||
return result;
|
||||
throw new PlaywrightException("Url must be String, Pattern or Predicate<String>, found: " + object.getClass().getTypeName());
|
||||
}
|
||||
|
||||
UrlMatcher(String url) {
|
||||
|
||||
@@ -18,13 +18,13 @@ package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.microsoft.playwright.FileChooser;
|
||||
import com.microsoft.playwright.Playwright;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
import com.microsoft.playwright.options.FilePayload;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
@@ -114,8 +114,8 @@ class Utils {
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
static FileChooser.FilePayload[] toFilePayloads(Path[] files) {
|
||||
List<FileChooser.FilePayload> payloads = new ArrayList<>();
|
||||
static FilePayload[] toFilePayloads(Path[] files) {
|
||||
List<FilePayload> payloads = new ArrayList<>();
|
||||
for (Path file : files) {
|
||||
byte[] buffer;
|
||||
try {
|
||||
@@ -123,13 +123,13 @@ class Utils {
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to read from file", e);
|
||||
}
|
||||
payloads.add(new FileChooser.FilePayload(file.getFileName().toString(), mimeType(file), buffer));
|
||||
payloads.add(new FilePayload(file.getFileName().toString(), mimeType(file), buffer));
|
||||
}
|
||||
return payloads.toArray(new FileChooser.FilePayload[0]);
|
||||
return payloads.toArray(new FilePayload[0]);
|
||||
}
|
||||
|
||||
static void writeToFile(byte[] buffer, Path path) {
|
||||
Path dir = path.getParent();
|
||||
static void mkParentDirs(Path file) {
|
||||
Path dir = file.getParent();
|
||||
if (dir != null) {
|
||||
if (!Files.exists(dir)) {
|
||||
try {
|
||||
@@ -139,13 +139,30 @@ class Utils {
|
||||
}
|
||||
}
|
||||
}
|
||||
try (DataOutputStream out = new DataOutputStream(new FileOutputStream(path.toFile()));) {
|
||||
}
|
||||
|
||||
static void writeToFile(byte[] buffer, Path path) {
|
||||
mkParentDirs(path);
|
||||
try (FileOutputStream out = new FileOutputStream(path.toFile())) {
|
||||
out.write(buffer);
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to write to file", e);
|
||||
}
|
||||
}
|
||||
|
||||
static void writeToFile(InputStream inputStream, Path path) {
|
||||
mkParentDirs(path);
|
||||
try (FileOutputStream out = new FileOutputStream(path.toFile())) {
|
||||
byte[] buf = new byte[8192];
|
||||
int length;
|
||||
while ((length = inputStream.read(buf)) > 0) {
|
||||
out.write(buf, 0, length);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new PlaywrightException("Failed to write to file", e);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isSafeCloseError(PlaywrightException exception) {
|
||||
return isSafeCloseError(exception.getMessage());
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.microsoft.playwright.Event;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
import com.microsoft.playwright.TimeoutError;
|
||||
|
||||
class WaitableResult<T> implements Waitable<T> {
|
||||
private T result;
|
||||
@@ -47,6 +48,9 @@ class WaitableResult<T> implements Waitable<T> {
|
||||
@Override
|
||||
public T get() {
|
||||
if (exception != null) {
|
||||
if (exception instanceof TimeoutError) {
|
||||
throw new TimeoutError(exception.getMessage(), exception);
|
||||
}
|
||||
throw new PlaywrightException(exception.getMessage(), exception);
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
import com.microsoft.playwright.TimeoutError;
|
||||
|
||||
class WaitableTimeout<T> implements Waitable<T> {
|
||||
private final long deadline;
|
||||
@@ -38,7 +38,7 @@ class WaitableTimeout<T> implements Waitable<T> {
|
||||
if (timeoutStr.endsWith(".0")) {
|
||||
timeoutStr = timeoutStr.substring(0, timeoutStr.length() - 2);
|
||||
}
|
||||
throw new PlaywrightException("Timeout " + timeoutStr + "ms exceeded");
|
||||
throw new TimeoutError("Timeout " + timeoutStr + "ms exceeded");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,11 +17,12 @@
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.*;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
import com.microsoft.playwright.WebSocket;
|
||||
import com.microsoft.playwright.WebSocketFrame;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
@@ -54,22 +55,22 @@ class WebSocketImpl extends ChannelOwner implements WebSocket {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFrameReceived(Consumer<FrameData> handler) {
|
||||
public void onFrameReceived(Consumer<WebSocketFrame> handler) {
|
||||
listeners.add(EventType.FRAMERECEIVED, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void offFrameReceived(Consumer<FrameData> handler) {
|
||||
public void offFrameReceived(Consumer<WebSocketFrame> handler) {
|
||||
listeners.remove(EventType.FRAMERECEIVED, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFrameSent(Consumer<FrameData> handler) {
|
||||
public void onFrameSent(Consumer<WebSocketFrame> handler) {
|
||||
listeners.add(EventType.FRAMESENT, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void offFrameSent(Consumer<FrameData> handler) {
|
||||
public void offFrameSent(Consumer<WebSocketFrame> handler) {
|
||||
listeners.remove(EventType.FRAMESENT, handler);
|
||||
}
|
||||
|
||||
@@ -84,7 +85,7 @@ class WebSocketImpl extends ChannelOwner implements WebSocket {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FrameData waitForFrameReceived(Runnable code, WaitForFrameReceivedOptions options) {
|
||||
public WebSocketFrame waitForFrameReceived(WaitForFrameReceivedOptions options, Runnable code) {
|
||||
if (options == null) {
|
||||
options = new WaitForFrameReceivedOptions();
|
||||
}
|
||||
@@ -92,21 +93,13 @@ class WebSocketImpl extends ChannelOwner implements WebSocket {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FrameData waitForFrameSent(Runnable code, WaitForFrameSentOptions options) {
|
||||
public WebSocketFrame waitForFrameSent(WaitForFrameSentOptions options, Runnable code) {
|
||||
if (options == null) {
|
||||
options = new WaitForFrameSentOptions();
|
||||
}
|
||||
return waitForEventWithTimeout(EventType.FRAMESENT, code, options.timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String waitForSocketError(Runnable code, WaitForSocketErrorOptions options) {
|
||||
if (options == null) {
|
||||
options = new WaitForSocketErrorOptions();
|
||||
}
|
||||
return waitForEventWithTimeout(EventType.SOCKETERROR, code, options.timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return isClosed;
|
||||
@@ -149,10 +142,10 @@ class WebSocketImpl extends ChannelOwner implements WebSocket {
|
||||
return runUntil(code, new WaitableRace<>(waitables));
|
||||
}
|
||||
|
||||
private static class FrameDataImpl implements FrameData {
|
||||
private static class WebSocketFrameImpl implements WebSocketFrame {
|
||||
private final byte[] bytes;
|
||||
|
||||
FrameDataImpl(String payload, boolean isBase64) {
|
||||
WebSocketFrameImpl(String payload, boolean isBase64) {
|
||||
if (isBase64) {
|
||||
bytes = Base64.getDecoder().decode(payload);
|
||||
} else {
|
||||
@@ -161,7 +154,7 @@ class WebSocketImpl extends ChannelOwner implements WebSocket {
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] body() {
|
||||
public byte[] binary() {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@@ -175,15 +168,15 @@ class WebSocketImpl extends ChannelOwner implements WebSocket {
|
||||
void handleEvent(String event, JsonObject parameters) {
|
||||
switch (event) {
|
||||
case "frameSent": {
|
||||
FrameDataImpl frameData = new FrameDataImpl(
|
||||
WebSocketFrameImpl WebSocketFrame = new WebSocketFrameImpl(
|
||||
parameters.get("data").getAsString(), parameters.get("opcode").getAsInt() == 2);
|
||||
listeners.notify(EventType.FRAMESENT, frameData);
|
||||
listeners.notify(EventType.FRAMESENT, WebSocketFrame);
|
||||
break;
|
||||
}
|
||||
case "frameReceived": {
|
||||
FrameDataImpl frameData = new FrameDataImpl(
|
||||
WebSocketFrameImpl WebSocketFrame = new WebSocketFrameImpl(
|
||||
parameters.get("data").getAsString(), parameters.get("opcode").getAsInt() == 2);
|
||||
listeners.notify(EventType.FRAMERECEIVED, frameData);
|
||||
listeners.notify(EventType.FRAMERECEIVED, WebSocketFrame);
|
||||
break;
|
||||
}
|
||||
case "socketError": {
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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.PlaywrightException;
|
||||
import org.java_websocket.client.WebSocketClient;
|
||||
import org.java_websocket.handshake.ServerHandshake;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
|
||||
class WebSocketTransport implements Transport {
|
||||
private final BlockingQueue<String> incoming = new ArrayBlockingQueue<>(1000);
|
||||
private final ClientConnection clientConnection;
|
||||
private boolean isClosed;
|
||||
private volatile Exception lastError;
|
||||
ListenerCollection<EventType> listeners = new ListenerCollection<>();
|
||||
|
||||
private enum EventType { CLOSE }
|
||||
|
||||
private class ClientConnection extends WebSocketClient {
|
||||
ClientConnection(URI serverUri) {
|
||||
super(serverUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(ServerHandshake handshakedata) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(String message) {
|
||||
incoming.add(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(int code, String reason, boolean remote) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception ex) {
|
||||
lastError = ex;
|
||||
}
|
||||
}
|
||||
|
||||
WebSocketTransport(URI uri, Duration timeout) {
|
||||
clientConnection = new ClientConnection(uri);
|
||||
try {
|
||||
if (!clientConnection.connectBlocking(timeout.toMillis(), TimeUnit.MILLISECONDS)) {
|
||||
throw new PlaywrightException("Failed to connect", lastError);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new PlaywrightException("Failed to connect", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(String message) {
|
||||
checkIfClosed();
|
||||
clientConnection.send(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String poll(Duration timeout) {
|
||||
checkIfClosed();
|
||||
try {
|
||||
return incoming.poll(timeout.toMillis(), TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
throw new PlaywrightException("Failed to read message", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (isClosed) {
|
||||
return;
|
||||
}
|
||||
isClosed = true;
|
||||
clientConnection.close();
|
||||
}
|
||||
|
||||
void onClose(Consumer<WebSocketTransport> handler) {
|
||||
listeners.add(EventType.CLOSE, handler);
|
||||
}
|
||||
|
||||
void offClose(Consumer<WebSocketTransport> handler) {
|
||||
listeners.remove(EventType.CLOSE, handler);
|
||||
}
|
||||
|
||||
private void checkIfClosed() {
|
||||
if (isClosed) {
|
||||
throw new PlaywrightException("Playwright connection closed");
|
||||
}
|
||||
if (clientConnection.isClosed()) {
|
||||
isClosed = true;
|
||||
listeners.notify(EventType.CLOSE, this);
|
||||
throw new PlaywrightException("Playwright connection closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,17 +16,16 @@
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.*;
|
||||
import com.microsoft.playwright.JSHandle;
|
||||
import com.microsoft.playwright.Worker;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.*;
|
||||
import static com.microsoft.playwright.impl.Utils.isFunctionBody;
|
||||
|
||||
class WorkerImpl extends ChannelOwner implements Worker {
|
||||
private final ListenerCollection<EventType> listeners = new ListenerCollection<>();
|
||||
@@ -59,7 +58,7 @@ class WorkerImpl extends ChannelOwner implements Worker {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Worker waitForClose(Runnable code, WaitForCloseOptions options) {
|
||||
public Worker waitForClose(WaitForCloseOptions options, Runnable code) {
|
||||
if (options == null) {
|
||||
options = new WaitForCloseOptions();
|
||||
}
|
||||
@@ -71,7 +70,6 @@ class WorkerImpl extends ChannelOwner implements Worker {
|
||||
return withLogging("Worker.evaluate", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.addProperty("isFunction", isFunctionBody(pageFunction));
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evaluateExpression", params);
|
||||
SerializedValue value = gson().fromJson(json.getAsJsonObject().get("value"), SerializedValue.class);
|
||||
@@ -84,7 +82,6 @@ class WorkerImpl extends ChannelOwner implements Worker {
|
||||
return withLogging("Worker.evaluateHandle", () -> {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("expression", pageFunction);
|
||||
params.addProperty("isFunction", isFunctionBody(pageFunction));
|
||||
params.add("arg", gson().toJsonTree(serializeArgument(arg)));
|
||||
JsonElement json = sendMessage("evaluateExpressionHandle", params);
|
||||
return connection.getExistingObject(json.getAsJsonObject().getAsJsonObject("handle").get("guid").getAsString());
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
import com.microsoft.playwright.BrowserContext;
|
||||
import com.microsoft.playwright.Frame;
|
||||
import com.microsoft.playwright.Page;
|
||||
|
||||
public interface BindingCallback {
|
||||
interface Source {
|
||||
BrowserContext context();
|
||||
Page page();
|
||||
Frame frame();
|
||||
}
|
||||
|
||||
Object call(Source source, Object... args);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public class BoundingBox {
|
||||
/**
|
||||
* the x coordinate of the element in pixels.
|
||||
*/
|
||||
public double x;
|
||||
/**
|
||||
* the y coordinate of the element in pixels.
|
||||
*/
|
||||
public double y;
|
||||
/**
|
||||
* the width of the element in pixels.
|
||||
*/
|
||||
public double width;
|
||||
/**
|
||||
* the height of the element in pixels.
|
||||
*/
|
||||
public double height;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public class Clip {
|
||||
/**
|
||||
* x-coordinate of top-left corner of clip area
|
||||
*/
|
||||
public double x;
|
||||
/**
|
||||
* y-coordinate of top-left corner of clip area
|
||||
*/
|
||||
public double y;
|
||||
/**
|
||||
* width of clipping area
|
||||
*/
|
||||
public double width;
|
||||
/**
|
||||
* height of clipping area
|
||||
*/
|
||||
public double height;
|
||||
|
||||
public Clip(double x, double y, double width, double height) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public enum ColorScheme {
|
||||
LIGHT,
|
||||
DARK,
|
||||
NO_PREFERENCE
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public class Cookie {
|
||||
public String name;
|
||||
public String value;
|
||||
/**
|
||||
* either url or domain / path are required. Optional.
|
||||
*/
|
||||
public String url;
|
||||
/**
|
||||
* either url or domain / path are required Optional.
|
||||
*/
|
||||
public String domain;
|
||||
/**
|
||||
* either url or domain / path are required Optional.
|
||||
*/
|
||||
public String path;
|
||||
/**
|
||||
* Unix time in seconds. Optional.
|
||||
*/
|
||||
public Double expires;
|
||||
/**
|
||||
* Optional.
|
||||
*/
|
||||
public Boolean httpOnly;
|
||||
/**
|
||||
* Optional.
|
||||
*/
|
||||
public Boolean secure;
|
||||
/**
|
||||
* Optional.
|
||||
*/
|
||||
public SameSiteAttribute sameSite;
|
||||
|
||||
public Cookie(String name, String value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
public Cookie setUrl(String url) {
|
||||
this.url = url;
|
||||
return this;
|
||||
}
|
||||
public Cookie setDomain(String domain) {
|
||||
this.domain = domain;
|
||||
return this;
|
||||
}
|
||||
public Cookie setPath(String path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
public Cookie setExpires(double expires) {
|
||||
this.expires = expires;
|
||||
return this;
|
||||
}
|
||||
public Cookie setHttpOnly(boolean httpOnly) {
|
||||
this.httpOnly = httpOnly;
|
||||
return this;
|
||||
}
|
||||
public Cookie setSecure(boolean secure) {
|
||||
this.secure = secure;
|
||||
return this;
|
||||
}
|
||||
public Cookie setSameSite(SameSiteAttribute sameSite) {
|
||||
this.sameSite = sameSite;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public enum ElementState {
|
||||
VISIBLE,
|
||||
HIDDEN,
|
||||
STABLE,
|
||||
ENABLED,
|
||||
DISABLED,
|
||||
EDITABLE
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public class FilePayload {
|
||||
/**
|
||||
* [File] name
|
||||
*/
|
||||
public String name;
|
||||
/**
|
||||
* [File] type
|
||||
*/
|
||||
public String mimeType;
|
||||
/**
|
||||
* File content
|
||||
*/
|
||||
public byte[] buffer;
|
||||
|
||||
public FilePayload(String name, String mimeType, byte[] buffer) {
|
||||
this.name = name;
|
||||
this.mimeType = mimeType;
|
||||
this.buffer = buffer;
|
||||
}
|
||||
}
|
||||
+3
-4
@@ -14,9 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright;
|
||||
package com.microsoft.playwright.options;
|
||||
|
||||
public interface Event<EventType> {
|
||||
EventType type();
|
||||
Object data();
|
||||
public interface FunctionCallback {
|
||||
Object call(Object... args);
|
||||
}
|
||||
+12
-20
@@ -14,36 +14,28 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright;
|
||||
package com.microsoft.playwright.options;
|
||||
|
||||
public class Geolocation {
|
||||
/**
|
||||
* Latitude between -90 and 90.
|
||||
*/
|
||||
public double latitude;
|
||||
/**
|
||||
* Longitude between -180 and 180.
|
||||
*/
|
||||
public double longitude;
|
||||
/**
|
||||
* Non-negative accuracy value. Defaults to {@code 0}.
|
||||
*/
|
||||
public Double accuracy;
|
||||
|
||||
public Geolocation() {
|
||||
}
|
||||
|
||||
public Geolocation(double latitude, double longitude) {
|
||||
this(latitude, longitude, null);
|
||||
}
|
||||
|
||||
public Geolocation(double latitude, double longitude, Double accuracy) {
|
||||
this.latitude = latitude;
|
||||
this.longitude = longitude;
|
||||
this.accuracy = accuracy;
|
||||
}
|
||||
|
||||
public Geolocation withLatitude(double latitude) {
|
||||
this.latitude = latitude;
|
||||
return this;
|
||||
}
|
||||
public Geolocation withLongitude(double longitude) {
|
||||
this.longitude = longitude;
|
||||
return this;
|
||||
}
|
||||
public Geolocation withAccuracy(double accuracy) {
|
||||
public Geolocation setAccuracy(double accuracy) {
|
||||
this.accuracy = accuracy;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public class HttpCredentials {
|
||||
public String username;
|
||||
public String password;
|
||||
|
||||
public HttpCredentials(String username, String password) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public enum KeyboardModifier {
|
||||
ALT,
|
||||
CONTROL,
|
||||
META,
|
||||
SHIFT
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public enum LoadState {
|
||||
LOAD,
|
||||
DOMCONTENTLOADED,
|
||||
NETWORKIDLE
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public class Margin {
|
||||
/**
|
||||
* Top margin, accepts values labeled with units. Defaults to {@code 0}.
|
||||
*/
|
||||
public String top;
|
||||
/**
|
||||
* Right margin, accepts values labeled with units. Defaults to {@code 0}.
|
||||
*/
|
||||
public String right;
|
||||
/**
|
||||
* Bottom margin, accepts values labeled with units. Defaults to {@code 0}.
|
||||
*/
|
||||
public String bottom;
|
||||
/**
|
||||
* Left margin, accepts values labeled with units. Defaults to {@code 0}.
|
||||
*/
|
||||
public String left;
|
||||
|
||||
public Margin setTop(String top) {
|
||||
this.top = top;
|
||||
return this;
|
||||
}
|
||||
public Margin setRight(String right) {
|
||||
this.right = right;
|
||||
return this;
|
||||
}
|
||||
public Margin setBottom(String bottom) {
|
||||
this.bottom = bottom;
|
||||
return this;
|
||||
}
|
||||
public Margin setLeft(String left) {
|
||||
this.left = left;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public enum Media {
|
||||
SCREEN,
|
||||
PRINT
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public enum MouseButton {
|
||||
LEFT,
|
||||
RIGHT,
|
||||
MIDDLE
|
||||
}
|
||||
+5
-17
@@ -14,26 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright;
|
||||
package com.microsoft.playwright.options;
|
||||
|
||||
public class Position {
|
||||
public int x;
|
||||
public int y;
|
||||
public double x;
|
||||
public double y;
|
||||
|
||||
public Position() {
|
||||
}
|
||||
|
||||
public Position(int x, int y) {
|
||||
public Position(double x, double y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public Position withX(int x) {
|
||||
this.x = x;
|
||||
return this;
|
||||
}
|
||||
public Position withY(int y) {
|
||||
this.y = y;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public class Proxy {
|
||||
/**
|
||||
* Proxy to be used for all requests. HTTP and SOCKS proxies are supported, for example {@code http://myproxy.com:3128} or
|
||||
* {@code socks5://myproxy.com:3128}. Short form {@code myproxy.com:3128} is considered an HTTP proxy.
|
||||
*/
|
||||
public String server;
|
||||
/**
|
||||
* Optional coma-separated domains to bypass proxy, for example {@code ".com, chromium.org, .domain.com"}.
|
||||
*/
|
||||
public String bypass;
|
||||
/**
|
||||
* Optional username to use if HTTP proxy requires authentication.
|
||||
*/
|
||||
public String username;
|
||||
/**
|
||||
* Optional password to use if HTTP proxy requires authentication.
|
||||
*/
|
||||
public String password;
|
||||
|
||||
public Proxy(String server) {
|
||||
this.server = server;
|
||||
}
|
||||
public Proxy setBypass(String bypass) {
|
||||
this.bypass = bypass;
|
||||
return this;
|
||||
}
|
||||
public Proxy setUsername(String username) {
|
||||
this.username = username;
|
||||
return this;
|
||||
}
|
||||
public Proxy setPassword(String password) {
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public class RecordVideoSize {
|
||||
/**
|
||||
* Video frame width.
|
||||
*/
|
||||
public int width;
|
||||
/**
|
||||
* Video frame height.
|
||||
*/
|
||||
public int height;
|
||||
|
||||
public RecordVideoSize(int width, int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public enum SameSiteAttribute {
|
||||
STRICT,
|
||||
LAX,
|
||||
NONE
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public enum ScreenshotType {
|
||||
PNG,
|
||||
JPEG
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public class SelectOption {
|
||||
/**
|
||||
* Matches by {@code option.value}. Optional.
|
||||
*/
|
||||
public String value;
|
||||
/**
|
||||
* Matches by {@code option.label}. Optional.
|
||||
*/
|
||||
public String label;
|
||||
/**
|
||||
* Matches by the index. Optional.
|
||||
*/
|
||||
public Integer index;
|
||||
|
||||
public SelectOption setValue(String value) {
|
||||
this.value = value;
|
||||
return this;
|
||||
}
|
||||
public SelectOption setLabel(String label) {
|
||||
this.label = label;
|
||||
return this;
|
||||
}
|
||||
public SelectOption setIndex(int index) {
|
||||
this.index = index;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user