diff --git a/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java b/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java index 9f891f32..4eb1e829 100644 --- a/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java +++ b/api-generator/src/main/java/com/microsoft/playwright/tools/ApiGenerator.java @@ -531,6 +531,22 @@ class Interface extends TypeDefinition { output.add(offset + " return height;"); output.add(offset + " }"); output.add(offset + "}"); + output.add(""); + + output.add(offset + "interface Function {"); + output.add(offset + " Object call(Object... args);"); + output.add(offset + "}"); + output.add(""); + + output.add(offset + "interface Binding {"); + output.add(offset + " interface Source {"); + output.add(offset + " BrowserContext context();"); + output.add(offset + " Page page();"); + output.add(offset + " Frame frame();"); + output.add(offset + " }"); + output.add(""); + output.add(offset + " Object call(Source source, Object... args);"); + output.add(offset + "}"); break; } case "BrowserContext": { diff --git a/api-generator/src/main/java/com/microsoft/playwright/tools/Types.java b/api-generator/src/main/java/com/microsoft/playwright/tools/Types.java index 2d0ecdaa..2d0d67ea 100644 --- a/api-generator/src/main/java/com/microsoft/playwright/tools/Types.java +++ b/api-generator/src/main/java/com/microsoft/playwright/tools/Types.java @@ -152,20 +152,19 @@ class Types { // js functions are always passed as text in java. - add("BrowserContext.exposeBinding.playwrightBinding", "function", "String"); - add("BrowserContext.exposeFunction.playwrightFunction", "function", "String"); add("Page.$eval.pageFunction", "function(Element)", "String"); add("Page.$$eval.pageFunction", "function(Array)", "String"); - add("Page.exposeBinding.playwrightBinding", "function", "String"); - add("Page.exposeFunction.playwrightFunction", "function", "String"); add("Frame.$eval.pageFunction", "function(Element)", "String"); add("Frame.$$eval.pageFunction", "function(Array)", "String"); add("ElementHandle.$eval.pageFunction", "function(Element)", "String"); add("ElementHandle.$$eval.pageFunction", "function(Array)", "String"); add("ElementHandle.evaluate.pageFunction", "function", "String"); add("JSHandle.evaluate.pageFunction", "function", "String"); - add("ChromiumBrowserContext.exposeBinding.playwrightBinding", "function", "String"); - add("ChromiumBrowserContext.exposeFunction.playwrightFunction", "function", "String"); + + add("BrowserContext.exposeBinding.playwrightBinding", "function", "Page.Binding"); + add("BrowserContext.exposeFunction.playwrightFunction", "function", "Page.Function"); + add("Page.exposeBinding.playwrightBinding", "function", "Binding"); + add("Page.exposeFunction.playwrightFunction", "function", "Function"); add("BrowserContext.addInitScript.script", "function|string|Object", "String"); add("Page.addInitScript.script", "function|string|Object", "String"); diff --git a/lib/src/main/java/com/microsoft/playwright/BrowserContext.java b/lib/src/main/java/com/microsoft/playwright/BrowserContext.java index 46560ef1..4023fbc5 100644 --- a/lib/src/main/java/com/microsoft/playwright/BrowserContext.java +++ b/lib/src/main/java/com/microsoft/playwright/BrowserContext.java @@ -78,8 +78,8 @@ public interface BrowserContext { return cookies(null); } List cookies(String urls); - void exposeBinding(String name, String playwrightBinding); - void exposeFunction(String name, String playwrightFunction); + void exposeBinding(String name, Page.Binding playwrightBinding); + void exposeFunction(String name, Page.Function playwrightFunction); default void grantPermissions(List permissions) { grantPermissions(permissions, null); } diff --git a/lib/src/main/java/com/microsoft/playwright/Page.java b/lib/src/main/java/com/microsoft/playwright/Page.java index 229024b1..2ee041b0 100644 --- a/lib/src/main/java/com/microsoft/playwright/Page.java +++ b/lib/src/main/java/com/microsoft/playwright/Page.java @@ -38,6 +38,20 @@ public interface Page { } } + interface Function { + Object call(Object... args); + } + + interface Binding { + interface Source { + BrowserContext context(); + Page page(); + Frame frame(); + } + + Object call(Source source, Object... args); + } + enum LoadState { DOMCONTENTLOADED, LOAD, NETWORKIDLE } class CloseOptions { public Boolean runBeforeUnload; @@ -789,8 +803,8 @@ public interface Page { return evaluateHandle(pageFunction, null); } JSHandle evaluateHandle(String pageFunction, Object arg); - void exposeBinding(String name, String playwrightBinding); - void exposeFunction(String name, String playwrightFunction); + void exposeBinding(String name, Binding playwrightBinding); + void exposeFunction(String name, Function playwrightFunction); default void fill(String selector, String value) { fill(selector, value, null); } diff --git a/lib/src/main/java/com/microsoft/playwright/impl/BindingCall.java b/lib/src/main/java/com/microsoft/playwright/impl/BindingCall.java new file mode 100644 index 00000000..9c9b7f27 --- /dev/null +++ b/lib/src/main/java/com/microsoft/playwright/impl/BindingCall.java @@ -0,0 +1,82 @@ +/** + * 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.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.microsoft.playwright.BrowserContext; +import com.microsoft.playwright.Frame; +import com.microsoft.playwright.Page; + +import java.util.ArrayList; +import java.util.List; + +import static com.microsoft.playwright.impl.Serialization.*; + +class BindingCall extends ChannelOwner { + private static class SourceImpl implements Page.Binding.Source { + private final Frame frame; + + public SourceImpl(Frame frame) { + this.frame = frame; + } + + @Override + public BrowserContext context() { + return page().context(); + } + + @Override + public Page page() { + return frame.page(); + } + + @Override + public Frame frame() { + return frame; + } + } + + BindingCall(ChannelOwner parent, String type, String guid, JsonObject initializer) { + super(parent, type, guid, initializer); + } + + String name() { + return initializer.get("name").getAsString(); + } + + void call(Page.Binding binding) { + try { + Frame frame = connection.getExistingObject(initializer.getAsJsonObject("frame").get("guid").getAsString()); + Page.Binding.Source source = new SourceImpl(frame); + List args = new ArrayList<>(); + for (JsonElement arg : initializer.getAsJsonArray("args")) { + args.add(deserialize(new Gson().fromJson(arg, SerializedValue.class))); + } + Object result = binding.call(source, args.toArray()); + + JsonObject params = new JsonObject(); + params.add("result", new Gson().toJsonTree(serializeArgument(result))); + sendMessage("resolve", params); + } catch (RuntimeException exception) { + JsonObject params = new JsonObject(); + params.add("error", new Gson().toJsonTree(serializeError(exception))); + sendMessage("reject", params); + } + } +} diff --git a/lib/src/main/java/com/microsoft/playwright/impl/BrowserContextImpl.java b/lib/src/main/java/com/microsoft/playwright/impl/BrowserContextImpl.java index b199823a..c2bdbd26 100644 --- a/lib/src/main/java/com/microsoft/playwright/impl/BrowserContextImpl.java +++ b/lib/src/main/java/com/microsoft/playwright/impl/BrowserContextImpl.java @@ -21,18 +21,22 @@ import com.google.gson.JsonObject; import com.microsoft.playwright.*; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.BiConsumer; import java.util.function.Supplier; -import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import static com.microsoft.playwright.impl.Utils.globToRegex; +import static com.microsoft.playwright.impl.Utils.isFunctionBody; class BrowserContextImpl extends ChannelOwner implements BrowserContext { + private final List pages = new ArrayList<>(); private List routes = new ArrayList<>(); + final Map bindings = new HashMap(); + private class RouteInfo { private String url; private BiConsumer handler; @@ -61,7 +65,13 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext { @Override public void addInitScript(String script, Object arg) { - + // TODO: serialize arg + JsonObject params = new JsonObject(); + if (isFunctionBody(script)) { + script = "(" + script + ")()"; + } + params.addProperty("source", script); + sendMessage("addInitScript", params); } @Override @@ -85,13 +95,24 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext { } @Override - public void exposeBinding(String name, String playwrightBinding) { + public void exposeBinding(String name, Page.Binding playwrightBinding) { + if (bindings.containsKey(name)) { + throw new RuntimeException("Function " + name + " has already been registered"); + } + for (PageImpl page : pages) { + if (page.bindings.containsKey(name)) + throw new Error("Function " + name + " has already been registered in one of the pages"); + } + bindings.put(name, playwrightBinding); + JsonObject params = new JsonObject(); + params.addProperty("name", name); + sendMessage("exposeBinding", params); } @Override - public void exposeFunction(String name, String playwrightFunction) { - + public void exposeFunction(String name, Page.Function playwrightFunction) { + exposeBinding(name, (Page.Binding.Source source, Object... args) -> playwrightFunction.call(args)); } @Override @@ -191,6 +212,15 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext { } } route.continue_(); + } else if ("page".equals(event)) { + PageImpl page = connection.getExistingObject(params.getAsJsonObject("page").get("guid").getAsString()); + 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()); + if (binding != null) { + bindingCall.call(binding); + } } } } diff --git a/lib/src/main/java/com/microsoft/playwright/impl/Connection.java b/lib/src/main/java/com/microsoft/playwright/impl/Connection.java index 0a534ab8..4ac0892e 100644 --- a/lib/src/main/java/com/microsoft/playwright/impl/Connection.java +++ b/lib/src/main/java/com/microsoft/playwright/impl/Connection.java @@ -174,6 +174,9 @@ public class Connection { ChannelOwner result = null; // initializer = this._replaceGuidsWithChannels(initializer); switch (type) { + case "BindingCall": + result = new BindingCall(parent, type, guid, initializer); + break; case "BrowserType": result = new BrowserTypeImpl(parent, type, guid, initializer); break; diff --git a/lib/src/main/java/com/microsoft/playwright/impl/FrameImpl.java b/lib/src/main/java/com/microsoft/playwright/impl/FrameImpl.java index fbeb6bcd..6be7c0fd 100644 --- a/lib/src/main/java/com/microsoft/playwright/impl/FrameImpl.java +++ b/lib/src/main/java/com/microsoft/playwright/impl/FrameImpl.java @@ -23,10 +23,11 @@ import com.google.gson.JsonObject; import com.microsoft.playwright.*; import java.util.*; -import java.util.function.Supplier; import static com.microsoft.playwright.Frame.LoadState.*; -import static com.microsoft.playwright.impl.Helpers.isFunctionBody; +import static com.microsoft.playwright.impl.Serialization.deserialize; +import static com.microsoft.playwright.impl.Serialization.serializeArgument; +import static com.microsoft.playwright.impl.Utils.isFunctionBody; public class FrameImpl extends ChannelOwner implements Frame { PageImpl page; @@ -49,99 +50,6 @@ public class FrameImpl extends ChannelOwner implements Frame { } } - private static SerializedValue serializeValue(Object value) { - SerializedValue result = new SerializedValue(); - if (value == null) - result.v = "undefined"; - else if (value instanceof Double) { - double d = ((Double) value).doubleValue(); - if (d == Double.POSITIVE_INFINITY) - result.v = "Infinity"; - else if (d == Double.NEGATIVE_INFINITY) - result.v = "-Infinity"; - else if (d == -0) - result.v = "-0"; - else if (Double.isNaN(d)) - result.v="NaN"; - else - result.n = d; - } -// if (value instanceof Date) - else if (value instanceof Boolean) - result.b = (Boolean) value; - else if (value instanceof Integer) - result.n = (Integer) value; - else if (value instanceof String) - result.s = (String) value; - else if (value instanceof List) { - List list = new ArrayList<>(); - for (Object o : (List) value) - list.add(serializeValue(o)); - result.a = list.toArray(new SerializedValue[0]); - } else if (value instanceof Map) { - List list = new ArrayList<>(); - Map map = (Map) value; - for (Map.Entry e : map.entrySet()) { - SerializedValue.O o = new SerializedValue.O(); - o.k = e.getKey(); - o.v = serializeValue(e.getValue()); - list.add(o); - } - result.o = list.toArray(new SerializedValue.O[0]); - } else - throw new RuntimeException("Unsupported type of argument: " + value); - return result; - } - private static SerializedArgument serializeArgument(Object arg) { - SerializedArgument result = new SerializedArgument(); - result.value = serializeValue(arg); - result.handles = new Channel[0]; - return result; - } - - - private static T deserialize(SerializedValue value) { - if (value.n != null) { - if (value.n.doubleValue() == (double) value.n.intValue()) - return (T) Integer.valueOf(value.n.intValue()); - return (T) Double.valueOf(value.n.doubleValue()); - } - if (value.b != null) - return (T) value.b; - if (value.s != null) - return (T) value.s; - if (value.v != null) { - switch (value.v) { - case "undefined": - case "null": - return null; - case "Infinity": - return (T) Double.valueOf(Double.POSITIVE_INFINITY); - case "-Infinity": - return (T) Double.valueOf(Double.NEGATIVE_INFINITY); - case "-0": - return (T) Double.valueOf(-0); - case "NaN": - return (T) Double.valueOf(Double.NaN); - default: - throw new RuntimeException("Unexpected value: " + value.v); - } - } - if (value.a != null) { - List list = new ArrayList(); - for (SerializedValue v : value.a) - list.add(deserialize(v)); - return (T) list; - } - if (value.o != null) { - Map map = new LinkedHashMap<>(); - for (SerializedValue.O o : value.o) - map.put(o.k, deserialize(o.v)); - return (T) map; - } - throw new RuntimeException("Unexpected result: " + new Gson().toJson(value)); - } - public T evalTyped(String expression) { return (T) evaluate(expression, null, false); } diff --git a/lib/src/main/java/com/microsoft/playwright/impl/Helpers.java b/lib/src/main/java/com/microsoft/playwright/impl/Helpers.java deleted file mode 100644 index 2922aa50..00000000 --- a/lib/src/main/java/com/microsoft/playwright/impl/Helpers.java +++ /dev/null @@ -1,26 +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; - -class Helpers { - static boolean isFunctionBody(String expression) { - expression = expression.trim(); - return expression.startsWith("function") || - expression.startsWith("async ") || - expression.contains("=>"); - } -} diff --git a/lib/src/main/java/com/microsoft/playwright/impl/PageImpl.java b/lib/src/main/java/com/microsoft/playwright/impl/PageImpl.java index f2e83af3..c175ee2a 100644 --- a/lib/src/main/java/com/microsoft/playwright/impl/PageImpl.java +++ b/lib/src/main/java/com/microsoft/playwright/impl/PageImpl.java @@ -16,15 +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.*; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.Future; import java.util.function.BiConsumer; import java.util.function.Supplier; @@ -32,14 +30,17 @@ import static com.microsoft.playwright.impl.Utils.convertViaJson; public class PageImpl extends ChannelOwner implements Page { + private final BrowserContextImpl browserContext; private final FrameImpl mainFrame; private final KeyboardImpl keyboard; private final MouseImpl mouse; private final List dialogHandlers = new ArrayList<>(); private final List> consoleListeners = new ArrayList<>(); + final Map bindings = new HashMap(); PageImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) { super(parent, type, guid, initializer); + browserContext = (BrowserContextImpl) parent; mainFrame = connection.getExistingObject(initializer.getAsJsonObject("mainFrame").get("guid").getAsString()); mainFrame.page = this; keyboard = new KeyboardImpl(this); @@ -202,13 +203,23 @@ public class PageImpl extends ChannelOwner implements Page { } @Override - public void exposeBinding(String name, String playwrightBinding) { + public void exposeBinding(String name, Binding playwrightBinding) { + if (bindings.containsKey(name)) { + throw new RuntimeException("Function " + name + " has already been registered"); + } + if (browserContext.bindings.containsKey(name)) { + throw new RuntimeException("Function " + name + " has already been registered in the browser context"); + } + bindings.put(name, playwrightBinding); + JsonObject params = new JsonObject(); + params.addProperty("name", name); + sendMessage("exposeBinding", params); } @Override - public void exposeFunction(String name, String playwrightFunction) { - + public void exposeFunction(String name, Function playwrightFunction) { + exposeBinding(name, (Binding.Source source, Object... args) -> playwrightFunction.call(args)); } @Override diff --git a/lib/src/main/java/com/microsoft/playwright/impl/Serialization.java b/lib/src/main/java/com/microsoft/playwright/impl/Serialization.java new file mode 100644 index 00000000..9f806e30 --- /dev/null +++ b/lib/src/main/java/com/microsoft/playwright/impl/Serialization.java @@ -0,0 +1,132 @@ +/** + * 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.Gson; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +class Serialization { + static SerializedError serializeError(Throwable e) { + SerializedError result = new SerializedError(); + result.error = new SerializedError.Error(); + result.error.message = e.getMessage(); + result.error.name = e.getClass().getName(); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + e.printStackTrace(new PrintStream(out)); + result.error.stack = new String(out.toByteArray()); + return result; + } + + private static SerializedValue serializeValue(Object value) { + SerializedValue result = new SerializedValue(); + if (value == null) + result.v = "undefined"; + else if (value instanceof Double) { + double d = ((Double) value).doubleValue(); + if (d == Double.POSITIVE_INFINITY) + result.v = "Infinity"; + else if (d == Double.NEGATIVE_INFINITY) + result.v = "-Infinity"; + else if (d == -0) + result.v = "-0"; + else if (Double.isNaN(d)) + result.v="NaN"; + else + result.n = d; + } + else if (value instanceof Boolean) + result.b = (Boolean) value; + else if (value instanceof Integer) + result.n = (Integer) value; + else if (value instanceof String) + result.s = (String) value; + else if (value instanceof List) { + List list = new ArrayList<>(); + for (Object o : (List) value) + list.add(serializeValue(o)); + result.a = list.toArray(new SerializedValue[0]); + } else if (value instanceof Map) { + List list = new ArrayList<>(); + Map map = (Map) value; + for (Map.Entry e : map.entrySet()) { + SerializedValue.O o = new SerializedValue.O(); + o.k = e.getKey(); + o.v = serializeValue(e.getValue()); + list.add(o); + } + result.o = list.toArray(new SerializedValue.O[0]); + } else + throw new RuntimeException("Unsupported type of argument: " + value); + return result; + } + + static SerializedArgument serializeArgument(Object arg) { + SerializedArgument result = new SerializedArgument(); + result.value = serializeValue(arg); + result.handles = new Channel[0]; + return result; + } + + static T deserialize(SerializedValue value) { + if (value.n != null) { + if (value.n.doubleValue() == (double) value.n.intValue()) + return (T) Integer.valueOf(value.n.intValue()); + return (T) Double.valueOf(value.n.doubleValue()); + } + if (value.b != null) + return (T) value.b; + if (value.s != null) + return (T) value.s; + if (value.v != null) { + switch (value.v) { + case "undefined": + case "null": + return null; + case "Infinity": + return (T) Double.valueOf(Double.POSITIVE_INFINITY); + case "-Infinity": + return (T) Double.valueOf(Double.NEGATIVE_INFINITY); + case "-0": + return (T) Double.valueOf(-0); + case "NaN": + return (T) Double.valueOf(Double.NaN); + default: + throw new RuntimeException("Unexpected value: " + value.v); + } + } + if (value.a != null) { + List list = new ArrayList(); + for (SerializedValue v : value.a) + list.add(deserialize(v)); + return (T) list; + } + if (value.o != null) { + Map map = new LinkedHashMap<>(); + for (SerializedValue.O o : value.o) + map.put(o.k, deserialize(o.v)); + return (T) map; + } + throw new RuntimeException("Unexpected result: " + new Gson().toJson(value)); + } +} diff --git a/lib/src/main/java/com/microsoft/playwright/impl/Utils.java b/lib/src/main/java/com/microsoft/playwright/impl/Utils.java index 059a75a8..8d2bd9a9 100644 --- a/lib/src/main/java/com/microsoft/playwright/impl/Utils.java +++ b/lib/src/main/java/com/microsoft/playwright/impl/Utils.java @@ -27,6 +27,13 @@ class Utils { return new Gson().fromJson(json, t); } + static boolean isFunctionBody(String expression) { + expression = expression.trim(); + return expression.startsWith("function") || + expression.startsWith("async ") || + expression.contains("=>"); + } + static Set escapeGlobChars = new HashSet<>(Arrays.asList('/', '$', '^', '+', '.', '(', ')', '=', '!', '|')); static String globToRegex(String glob) { @@ -83,5 +90,4 @@ class Utils { tokens.append('$'); return tokens.toString(); } - } diff --git a/lib/src/test/java/com/microsoft/playwright/TestPopup.java b/lib/src/test/java/com/microsoft/playwright/TestPopup.java index bc53b967..e222acbc 100644 --- a/lib/src/test/java/com/microsoft/playwright/TestPopup.java +++ b/lib/src/test/java/com/microsoft/playwright/TestPopup.java @@ -196,4 +196,73 @@ public class TestPopup { assertEquals(mapOf("width", 600, "height", 300), size); assertEquals(mapOf("width", 500, "height", 400), resized); } + + @Test + void should_respect_routes_from_browser_context_with_window_open() { + BrowserContext context = browser.newContext(); + Page page = context.newPage(); + page.navigate(server.EMPTY_PAGE); + boolean[] intercepted = {false}; + context.route("**/empty.html", (route, request) -> { + route.continue_(); + intercepted[0] = true; + }); + Deferred popupEvent = page.waitForPopup(); + page.evaluate("url => window['__popup'] = window.open(url)", server.EMPTY_PAGE); + popupEvent.get(); + assertTrue(intercepted[0]); + context.close(); + } + + @Test + void BrowserContext_addInitScript_should_apply_to_an_in_process_popup() { + BrowserContext context = browser.newContext(); + context.addInitScript("() => window['injected'] = 123"); + Page page = context.newPage(); + page.navigate(server.EMPTY_PAGE); + Object injected = page.evaluate("() => {\n" + + " const win = window.open('about:blank');\n" + + " return win['injected'];\n" + + "}"); + context.close(); + assertEquals(123, injected); + } + + @Test + void BrowserContext_addInitScript_should_apply_to_a_cross_process_popup() { + BrowserContext context = browser.newContext(); + context.addInitScript("() => window['injected'] = 123"); + Page page = context.newPage(); + page.navigate(server.EMPTY_PAGE); + Deferred popupEvent = page.waitForPopup(); + page.evaluate("url => window.open(url)", server.CROSS_PROCESS_PREFIX + "/title.html"); + Page popup = popupEvent.get(); + assertEquals(123, popup.evaluate("injected")); + + popup.reload(); + + assertEquals(123, popup.evaluate("injected")); + context.close(); + } + + @Test + void should_expose_function_from_browser_context() { + BrowserContext context = browser.newContext(); + List messages = new ArrayList<>(); + context.exposeFunction("add", args -> { + messages.add("binding"); + return (int) args[0] + (int) args[1]; + }); + Page page = context.newPage(); +// context.on("page", () => messages.push('page')); + page.navigate(server.EMPTY_PAGE); + Object added = page.evaluate("async () => {\n" + + " const win = window.open('about:blank');\n" + + " return win['add'](9, 4);\n" + + "}"); + context.close(); + assertEquals(13, added); +// assertEquals(messages.join("|"), "page|binding"); + } + }