From f3507ce621284aa3cc625b6dea0d232b050f62a9 Mon Sep 17 00:00:00 2001 From: Puja Jagani Date: Tue, 7 Feb 2023 11:21:53 +0530 Subject: [PATCH] [java][cdp] Use devtools script pinning mechanism by default This helps achieve feature parity in script pinning among different Selenium language bindings. --- .../openqa/selenium/UnpinnedScriptKey.java | 34 ++++++++- .../selenium/remote/RemoteWebDriver.java | 74 +++++++++++++++++++ 2 files changed, 105 insertions(+), 3 deletions(-) diff --git a/java/src/org/openqa/selenium/UnpinnedScriptKey.java b/java/src/org/openqa/selenium/UnpinnedScriptKey.java index c2aa6b9bbbf62..d6806908472ef 100644 --- a/java/src/org/openqa/selenium/UnpinnedScriptKey.java +++ b/java/src/org/openqa/selenium/UnpinnedScriptKey.java @@ -21,12 +21,15 @@ import java.util.HashSet; import java.util.Objects; import java.util.Set; +import java.util.UUID; import java.util.WeakHashMap; -class UnpinnedScriptKey extends ScriptKey { +public class UnpinnedScriptKey extends ScriptKey { private static final WeakHashMap> pinnedScripts = new WeakHashMap<>(); private final String script; + private String scriptId; + private final String scriptHandle; static UnpinnedScriptKey pin(JavascriptExecutor executor, String script) { UnpinnedScriptKey toReturn = new UnpinnedScriptKey(script); @@ -50,16 +53,41 @@ static Set getPinnedScripts(JavascriptExecutor executor) { return Collections.unmodifiableSet(toReturn); } - private UnpinnedScriptKey(String script) { + public UnpinnedScriptKey(String script) { super(script); + this.scriptHandle = UUID.randomUUID().toString().replace("-", ""); this.script = script; } - String getScript() { + public void setScriptId(String id) { + this.scriptId = id; + } + + public String getScriptId() { + return this.scriptId; + } + + public String getScript() { return script; } + public String getScriptHandle() { + return scriptHandle; + } + + public String creationScript() { + return String.format("function __webdriver_%s(arguments) { %s }", this.scriptHandle, this.script); + } + + public String executionScript() { + return String.format("return __webdriver_%s(arguments)", this.scriptHandle); + } + + public String removalScript() { + return String.format("__webdriver_%s = undefined", this.scriptHandle); + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/java/src/org/openqa/selenium/remote/RemoteWebDriver.java b/java/src/org/openqa/selenium/remote/RemoteWebDriver.java index 9a520cd93dece..d4edb8325e44a 100644 --- a/java/src/org/openqa/selenium/remote/RemoteWebDriver.java +++ b/java/src/org/openqa/selenium/remote/RemoteWebDriver.java @@ -29,6 +29,7 @@ import org.openqa.selenium.Dimension; import org.openqa.selenium.HasCapabilities; import org.openqa.selenium.ImmutableCapabilities; +import org.openqa.selenium.JavascriptException; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.MutableCapabilities; import org.openqa.selenium.NoSuchFrameException; @@ -38,9 +39,11 @@ import org.openqa.selenium.Platform; import org.openqa.selenium.Point; import org.openqa.selenium.PrintsPage; +import org.openqa.selenium.ScriptKey; import org.openqa.selenium.SearchContext; import org.openqa.selenium.SessionNotCreatedException; import org.openqa.selenium.TakesScreenshot; +import org.openqa.selenium.UnpinnedScriptKey; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.WebElement; @@ -52,6 +55,7 @@ import org.openqa.selenium.interactions.Interactive; import org.openqa.selenium.interactions.Sequence; import org.openqa.selenium.internal.Require; +import org.openqa.selenium.json.TypeToken; import org.openqa.selenium.logging.LocalLogs; import org.openqa.selenium.logging.LoggingHandler; import org.openqa.selenium.logging.Logs; @@ -493,6 +497,76 @@ public Object executeAsyncScript(String script, Object... args) { return execute(DriverCommand.EXECUTE_ASYNC_SCRIPT(script, convertedArgs)).getValue(); } + @Override + public ScriptKey pin(String script) { + UnpinnedScriptKey key = (UnpinnedScriptKey) JavascriptExecutor.super.pin(script); + String browserName = getCapabilities().getBrowserName().toLowerCase(); + if ((browserName.equals("chrome") || + browserName.equals("msedge") || + browserName.equals("microsoftedge")) && this instanceof HasDevTools) { + + ((HasDevTools) this).maybeGetDevTools().ifPresent(devTools -> { + devTools.createSessionIfThereIsNotOne(); + devTools.send(new org.openqa.selenium.devtools.Command<>("Page.enable", + ImmutableMap.of())); + devTools.send(new org.openqa.selenium.devtools.Command<>("Runtime.evaluate", + ImmutableMap.of("expression", + key.creationScript()))); + Map result = devTools.send(new org.openqa.selenium.devtools.Command<>( + "Page.addScriptToEvaluateOnNewDocument", + ImmutableMap.of("source", key.creationScript()), + new TypeToken>() { + }.getType())); + key.setScriptId((String) result.get("identifier")); + }); + } + return key; + } + + @Override + public void unpin(ScriptKey scriptKey) { + UnpinnedScriptKey key = (UnpinnedScriptKey) scriptKey; + + JavascriptExecutor.super.unpin(key); + + String browserName = getCapabilities().getBrowserName().toLowerCase(); + if ((browserName.equals("chrome") || + browserName.equals("msedge") || + browserName.equals("microsoftedge")) && this instanceof HasDevTools) { + ((HasDevTools) this).maybeGetDevTools().ifPresent(devTools -> { + devTools.send(new org.openqa.selenium.devtools.Command<>("Page.enable", + ImmutableMap.of())); + devTools.send(new org.openqa.selenium.devtools.Command<>( + "Runtime.evaluate", + ImmutableMap.of("expression", key.removalScript()))); + devTools.send(new org.openqa.selenium.devtools.Command<>( + "Page.removeScriptToEvaluateOnLoad", + ImmutableMap.of("identifier", key.getScriptId()))); + }); + } + } + + @Override + public Object executeScript(ScriptKey key, Object... args) { + Require.stateCondition( + key instanceof UnpinnedScriptKey, + "Script key should have been generated by this driver"); + + if (!getPinnedScripts().contains(key)) { + throw new JavascriptException("Script is unpinned"); + } + + String browserName = getCapabilities().getBrowserName().toLowerCase(); + + if ((browserName.equals("chrome") || + browserName.equals("msedge") || + browserName.equals("microsoftedge")) && this instanceof HasDevTools) { + return executeScript(((UnpinnedScriptKey) key).executionScript(), args); + } + + return executeScript(((UnpinnedScriptKey) key).getScript(), args); + } + private boolean isJavascriptEnabled() { return getCapabilities().is(SUPPORTS_JAVASCRIPT); }