Chrome DevTools Protocol version 99 support library for Selenium WebDriver Java bindings
—
Browser target (tab/window/iframe) management including attachment, detachment, and target enumeration for multi-context automation. Enables advanced browser automation scenarios involving multiple tabs, windows, and execution contexts.
Creates a target management handler for Chrome DevTools Protocol v99.
/**
* Target management implementation for CDP v99
* Implements the idealized Target interface
*/
public class V99Target implements org.openqa.selenium.devtools.idealized.target.Target;Attach to and detach from browser targets for multi-context automation.
/**
* Attach to a specific target for DevTools communication
* @param targetId - ID of target to attach to
* @return Command returning session ID for the attached target
*/
public Command<SessionID> attachToTarget(TargetID targetId);
/**
* Detach from a target, ending DevTools communication
* @param sessionId - Optional session ID to detach
* @param targetId - Optional target ID to detach
* @return Command to detach from target
*/
public Command<Void> detachFromTarget(Optional<SessionID> sessionId, Optional<TargetID> targetId);
/**
* Enable automatic attachment to new targets
* @return Command to enable auto-attach
*/
public Command<Void> setAutoAttach();List and discover available browser targets.
/**
* Get list of all available targets
* @return Command returning list of target information
*/
public Command<List<TargetInfo>> getTargets();Monitor target lifecycle events.
/**
* Get event for when targets are detached
* @return Event for target detachment
*/
public Event<TargetID> detached();/**
* Unique identifier for browser targets
*/
public class TargetID {
/**
* Create target ID from string
* @param id - Target ID string
*/
public TargetID(String id);
/**
* Get target ID as string
* @return Target ID string
*/
public String toString();
}
/**
* Session identifier for DevTools connections
*/
public class SessionID {
/**
* Create session ID from string
* @param id - Session ID string
*/
public SessionID(String id);
/**
* Get session ID as string
* @return Session ID string
*/
public String toString();
}
/**
* Browser context identifier
*/
public class BrowserContextID {
/**
* Create browser context ID from string
* @param id - Context ID string
*/
public BrowserContextID(String id);
/**
* Get context ID as string
* @return Context ID string
*/
public String toString();
}/**
* Information about a browser target
*/
public class TargetInfo {
/**
* Create target information
* @param targetId - Target identifier
* @param type - Target type (page, background_page, etc.)
* @param title - Target title
* @param url - Target URL
* @param attached - Whether target is attached
* @param openerId - Optional opener target ID
* @param browserContextId - Optional browser context ID
*/
public TargetInfo(
TargetID targetId,
String type,
String title,
String url,
Boolean attached,
Optional<TargetID> openerId,
Optional<BrowserContextID> browserContextId
);
/**
* Get target ID
* @return Target identifier
*/
public TargetID getTargetId();
/**
* Get target type
* @return Type string (page, background_page, service_worker, etc.)
*/
public String getType();
/**
* Get target title (usually page title)
* @return Title string
*/
public String getTitle();
/**
* Get target URL
* @return URL string
*/
public String getUrl();
/**
* Check if target is attached
* @return True if attached to DevTools
*/
public Boolean getAttached();
/**
* Get opener target ID if target was opened by another target
* @return Optional opener target ID
*/
public Optional<TargetID> getOpenerId();
/**
* Get browser context ID
* @return Optional browser context ID
*/
public Optional<BrowserContextID> getBrowserContextId();
}Common target types you may encounter:
"page" - Regular web page/tab"background_page" - Extension background page"service_worker" - Service worker"shared_worker" - Shared worker"browser" - Browser-level target"other" - Other target typesimport org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.v99.V99Domains;
import org.openqa.selenium.devtools.idealized.target.model.TargetInfo;
DevTools devTools = ...; // from ChromeDriver
V99Domains domains = new V99Domains(devTools);
// Get all available targets
List<TargetInfo> targets = devTools.send(domains.target().getTargets());
System.out.println("Available targets:");
for (TargetInfo target : targets) {
System.out.printf(" %s: %s (%s) - %s%n",
target.getTargetId().toString(),
target.getTitle(),
target.getType(),
target.getUrl()
);
if (target.getAttached()) {
System.out.println(" [ATTACHED]");
}
}// Open a new tab
((JavascriptExecutor) driver).executeScript("window.open('about:blank', '_blank');");
// Get updated target list
List<TargetInfo> targets = devTools.send(domains.target().getTargets());
// Find the new tab (page type, not attached)
TargetInfo newTab = targets.stream()
.filter(target -> "page".equals(target.getType()))
.filter(target -> !target.getAttached())
.findFirst()
.orElseThrow(() -> new RuntimeException("No new tab found"));
System.out.println("Found new tab: " + newTab.getTargetId());
// Attach to the new tab
SessionID sessionId = devTools.send(domains.target().attachToTarget(newTab.getTargetId()));
System.out.println("Attached with session ID: " + sessionId);
// Later, detach from the tab
devTools.send(domains.target().detachFromTarget(Optional.of(sessionId), Optional.of(newTab.getTargetId())));// Enable auto-attach for new targets
devTools.send(domains.target().setAutoAttach());
// Monitor target detachment events
devTools.addListener(domains.target().detached(), targetId -> {
System.out.println("Target detached: " + targetId.toString());
// Clean up any resources associated with this target
cleanupTargetResources(targetId);
});
// Monitor for new targets (would require additional Target domain events)
// Note: V99Target doesn't expose target creation events directly
// You would need to poll getTargets() or use lower-level CDP events// Get all targets
List<TargetInfo> targets = devTools.send(domains.target().getTargets());
// Attach only to page targets that aren't already attached
List<SessionID> attachedSessions = new ArrayList<>();
for (TargetInfo target : targets) {
if ("page".equals(target.getType()) && !target.getAttached()) {
try {
SessionID sessionId = devTools.send(domains.target().attachToTarget(target.getTargetId()));
attachedSessions.add(sessionId);
System.out.printf("Attached to page '%s' with session %s%n",
target.getTitle(), sessionId.toString());
} catch (Exception e) {
System.err.println("Failed to attach to target " + target.getTargetId() + ": " + e.getMessage());
}
}
}
// Later, clean up all attachments
for (SessionID sessionId : attachedSessions) {
try {
devTools.send(domains.target().detachFromTarget(Optional.of(sessionId), Optional.empty()));
} catch (Exception e) {
System.err.println("Failed to detach session " + sessionId + ": " + e.getMessage());
}
}List<TargetInfo> targets = devTools.send(domains.target().getTargets());
// Classify targets by type
Map<String, List<TargetInfo>> targetsByType = targets.stream()
.collect(Collectors.groupingBy(TargetInfo::getType));
targetsByType.forEach((type, targetList) -> {
System.out.println(type + " targets (" + targetList.size() + "):");
targetList.forEach(target -> {
System.out.printf(" %s: %s%n", target.getTitle(), target.getUrl());
});
});
// Find main page targets (excluding extension pages, workers, etc.)
List<TargetInfo> pageTargets = targets.stream()
.filter(target -> "page".equals(target.getType()))
.filter(target -> !target.getUrl().startsWith("chrome-extension://"))
.collect(Collectors.toList());
System.out.println("Found " + pageTargets.size() + " page targets");List<TargetInfo> targets = devTools.send(domains.target().getTargets());
// Group targets by browser context
Map<Optional<BrowserContextID>, List<TargetInfo>> targetsByContext = targets.stream()
.collect(Collectors.groupingBy(TargetInfo::getBrowserContextId));
targetsByContext.forEach((contextId, targetList) -> {
String contextName = contextId.map(id -> id.toString()).orElse("default");
System.out.println("Browser context " + contextName + ":");
targetList.forEach(target -> {
System.out.printf(" %s (%s): %s%n",
target.getTitle(), target.getType(), target.getUrl());
});
});List<TargetInfo> targets = devTools.send(domains.target().getTargets());
// Build parent-child relationships
Map<TargetID, List<TargetInfo>> childTargets = targets.stream()
.filter(target -> target.getOpenerId().isPresent())
.collect(Collectors.groupingBy(target -> target.getOpenerId().get()));
// Find root targets (no opener)
List<TargetInfo> rootTargets = targets.stream()
.filter(target -> target.getOpenerId().isEmpty())
.collect(Collectors.toList());
// Display hierarchy
for (TargetInfo rootTarget : rootTargets) {
displayTargetHierarchy(rootTarget, childTargets, 0);
}
private void displayTargetHierarchy(TargetInfo target, Map<TargetID, List<TargetInfo>> childMap, int depth) {
String indent = " ".repeat(depth);
System.out.printf("%s%s (%s): %s%n", indent, target.getTitle(), target.getType(), target.getUrl());
List<TargetInfo> children = childMap.getOrDefault(target.getTargetId(), Collections.emptyList());
for (TargetInfo child : children) {
displayTargetHierarchy(child, childMap, depth + 1);
}
}Target management operations can encounter various issues:
Handle errors through proper exception handling:
try {
List<TargetInfo> targets = devTools.send(domains.target().getTargets());
for (TargetInfo target : targets) {
if (shouldAttachToTarget(target)) {
try {
SessionID sessionId = devTools.send(domains.target().attachToTarget(target.getTargetId()));
handleAttachedTarget(target.getTargetId(), sessionId);
} catch (DevToolsException e) {
System.err.println("Failed to attach to target " + target.getTargetId() + ": " + e.getMessage());
// Continue with other targets
}
}
}
} catch (DevToolsException e) {
System.err.println("Failed to get targets: " + e.getMessage());
}getTargets() calls can impact performanceInstall with Tessl CLI
npx tessl i tessl/maven-org-seleniumhq-selenium--selenium-devtools-v99