Java bindings for Chrome DevTools Protocol version 102, providing programmatic access to Chrome browser debugging and automation capabilities
—
Manages browser targets (tabs, windows, workers), attachment/detachment operations, and target information retrieval. This domain enables control over multiple browser contexts and automation of complex multi-target scenarios.
Main class for browser target management. Implements the idealized Target interface to provide v102-specific implementations for target operations and information retrieval.
/**
* Manages browser targets (tabs, windows, workers)
*/
public class V102Target implements org.openqa.selenium.devtools.idealized.target.Target {
/**
* Detaches from a specific target using session ID or target ID
* @param sessionId Optional session identifier for the target connection
* @param targetId Optional target identifier to detach from
* @return Command to detach from target
*/
public Command<Void> detachFromTarget(Optional<SessionID> sessionId, Optional<TargetID> targetId);
/**
* Retrieves information about all available targets
* @return Command returning list of target information objects
*/
public Command<List<org.openqa.selenium.devtools.idealized.target.model.TargetInfo>> getTargets();
/**
* Attaches to a specific target for DevTools communication
* @param targetId Target identifier to attach to
* @return Command returning session ID for the new attachment
*/
public Command<SessionID> attachToTarget(TargetID targetId);
/**
* Sets automatic attachment for new targets as they are created
* Enables automatic DevTools connection to new tabs/windows
* @return Command to enable auto-attach for new targets
*/
public Command<Void> setAutoAttach();
/**
* Returns the target detached event for monitoring target disconnections
* @return Event for target detachment notifications
*/
public Event<TargetID> detached();
}Usage Examples:
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.v102.V102Domains;
import org.openqa.selenium.devtools.idealized.target.model.TargetInfo;
import org.openqa.selenium.devtools.idealized.target.model.TargetID;
import org.openqa.selenium.devtools.idealized.target.model.SessionID;
import java.util.List;
// Setup
ChromeDriver driver = new ChromeDriver();
DevTools devTools = driver.getDevTools();
devTools.createSession();
V102Domains domains = new V102Domains(devTools);
// Get all available targets
List<TargetInfo> targets = devTools.send(domains.target().getTargets());
System.out.println("Available targets:");
for (TargetInfo target : targets) {
System.out.println(String.format(
"Target: %s - %s (%s) - %s",
target.getTargetId(),
target.getTitle(),
target.getType(),
target.getUrl()
));
}
// Find page targets
targets.stream()
.filter(target -> "page".equals(target.getType()))
.forEach(target -> {
System.out.println("Page target: " + target.getTitle() + " - " + target.getUrl());
});// Attach to specific targets for independent communication
List<TargetInfo> targets = devTools.send(domains.target().getTargets());
for (TargetInfo target : targets) {
if ("page".equals(target.getType()) && !target.getAttached()) {
// Attach to unattached page targets
SessionID sessionId = devTools.send(domains.target().attachToTarget(target.getTargetId()));
System.out.println("Attached to target " + target.getTargetId() + " with session " + sessionId);
// Now you can send commands to this specific target using the sessionId
// (This requires more advanced DevTools session management)
}
}// Enable automatic attachment to new targets
devTools.send(domains.target().setAutoAttach());
// Listen for target detachment events
devTools.addListener(domains.target().detached(), (targetId) -> {
System.out.println("Target detached: " + targetId);
});
// Open new tab - will be automatically attached
driver.executeScript("window.open('https://example.com', '_blank');");
// List targets again to see the new one
List<TargetInfo> updatedTargets = devTools.send(domains.target().getTargets());
System.out.println("Targets after opening new tab: " + updatedTargets.size());import java.util.Map;
import java.util.HashMap;
// Track attached targets and their sessions
Map<TargetID, SessionID> attachedTargets = new HashMap<>();
// Get initial targets
List<TargetInfo> targets = devTools.send(domains.target().getTargets());
// Attach to all page targets
for (TargetInfo target : targets) {
if ("page".equals(target.getType()) && !target.getAttached()) {
try {
SessionID sessionId = devTools.send(domains.target().attachToTarget(target.getTargetId()));
attachedTargets.put(target.getTargetId(), sessionId);
System.out.println("Attached to: " + target.getTitle());
} catch (Exception e) {
System.err.println("Failed to attach to target " + target.getTargetId() + ": " + e.getMessage());
}
}
}
// Monitor for target detachment
devTools.addListener(domains.target().detached(), (targetId) -> {
if (attachedTargets.containsKey(targetId)) {
SessionID sessionId = attachedTargets.remove(targetId);
System.out.println("Target " + targetId + " detached (was session " + sessionId + ")");
}
});
// Detach from specific targets when done
for (Map.Entry<TargetID, SessionID> entry : attachedTargets.entrySet()) {
devTools.send(domains.target().detachFromTarget(
Optional.of(entry.getValue()),
Optional.of(entry.getKey())
));
}import java.util.Optional;
// Find specific types of targets
List<TargetInfo> targets = devTools.send(domains.target().getTargets());
// Find the main page target
Optional<TargetInfo> mainPage = targets.stream()
.filter(target -> "page".equals(target.getType()))
.filter(target -> target.getUrl().startsWith("https://"))
.findFirst();
if (mainPage.isPresent()) {
System.out.println("Main page: " + mainPage.get().getTitle());
}
// Find service worker targets
List<TargetInfo> serviceWorkers = targets.stream()
.filter(target -> "service_worker".equals(target.getType()))
.collect(Collectors.toList());
System.out.println("Service workers: " + serviceWorkers.size());
// Find background pages (extensions)
List<TargetInfo> backgroundPages = targets.stream()
.filter(target -> "background_page".equals(target.getType()))
.collect(Collectors.toList());
System.out.println("Background pages: " + backgroundPages.size());The V102Target class interacts with generated CDP protocol classes:
// Generated CDP target classes (available at runtime)
class Target {
static Command<GetTargetsResponse> getTargets();
static Command<AttachToTargetResponse> attachToTarget(TargetID targetId, boolean flatten);
static Command<Void> detachFromTarget(Optional<SessionID> sessionId, Optional<TargetID> targetId);
static Command<Void> setAutoAttach(boolean autoAttach, boolean waitForDebuggerOnStart, Optional<Boolean> flatten);
static Event<DetachedFromTargetEvent> detachedFromTarget();
}
// Target information from CDP
class TargetInfo {
TargetID getTargetId();
String getType();
String getTitle();
String getUrl();
boolean getAttached();
Optional<TargetID> getOpenerId();
Optional<BrowserContextID> getBrowserContextId();
}
// Target and session identifiers
class TargetID {
TargetID(String id);
String toString();
}
class SessionID {
SessionID(String id);
String toString();
}
class BrowserContextID {
BrowserContextID(String id);
String toString();
}// Detachment event data
class DetachedFromTargetEvent {
Optional<SessionID> getSessionId();
Optional<TargetID> getTargetId();
}Different types of targets you may encounter:
// Monitor complete target lifecycle
devTools.send(domains.target().setAutoAttach());
// Track target creation through getTargets polling
Set<TargetID> knownTargets = new HashSet<>();
List<TargetInfo> initialTargets = devTools.send(domains.target().getTargets());
initialTargets.forEach(target -> knownTargets.add(target.getTargetId()));
// Listen for detachment
devTools.addListener(domains.target().detached(), (targetId) -> {
knownTargets.remove(targetId);
System.out.println("Target lifecycle ended: " + targetId);
});
// Periodically check for new targets
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
try {
List<TargetInfo> currentTargets = devTools.send(domains.target().getTargets());
for (TargetInfo target : currentTargets) {
if (!knownTargets.contains(target.getTargetId())) {
knownTargets.add(target.getTargetId());
System.out.println("New target detected: " + target.getTitle());
}
}
} catch (Exception e) {
System.err.println("Error checking targets: " + e.getMessage());
}
}, 1, 1, TimeUnit.SECONDS);// Attach only to specific target types or URLs
List<TargetInfo> targets = devTools.send(domains.target().getTargets());
for (TargetInfo target : targets) {
boolean shouldAttach = false;
// Attach to main application pages
if ("page".equals(target.getType()) &&
target.getUrl().contains("myapp.com")) {
shouldAttach = true;
}
// Attach to service workers for the application
if ("service_worker".equals(target.getType()) &&
target.getUrl().contains("myapp.com")) {
shouldAttach = true;
}
if (shouldAttach && !target.getAttached()) {
try {
SessionID sessionId = devTools.send(domains.target().attachToTarget(target.getTargetId()));
System.out.println("Attached to " + target.getType() + ": " + target.getTitle());
} catch (Exception e) {
System.err.println("Failed to attach to target: " + e.getMessage());
}
}
}// Analyze target relationships and hierarchy
List<TargetInfo> targets = devTools.send(domains.target().getTargets());
// Group targets by opener (parent-child relationships)
Map<Optional<TargetID>, List<TargetInfo>> targetsByOpener = targets.stream()
.collect(Collectors.groupingBy(TargetInfo::getOpenerId));
// Print target hierarchy
targetsByOpener.forEach((openerId, targetList) -> {
if (openerId.isPresent()) {
System.out.println("Targets opened by " + openerId.get() + ":");
} else {
System.out.println("Root targets:");
}
targetList.forEach(target -> {
System.out.println(" " + target.getType() + ": " + target.getTitle());
});
});
// Find browser context information
targets.forEach(target -> {
target.getBrowserContextId().ifPresent(contextId -> {
System.out.println("Target " + target.getTargetId() + " in context " + contextId);
});
});// Robust target attachment with error handling
public SessionID safeAttachToTarget(TargetID targetId) {
try {
// Verify target still exists
List<TargetInfo> targets = devTools.send(domains.target().getTargets());
boolean targetExists = targets.stream()
.anyMatch(target -> target.getTargetId().equals(targetId));
if (!targetExists) {
throw new IllegalArgumentException("Target " + targetId + " no longer exists");
}
// Attempt attachment
SessionID sessionId = devTools.send(domains.target().attachToTarget(targetId));
System.out.println("Successfully attached to target " + targetId);
return sessionId;
} catch (Exception e) {
System.err.println("Failed to attach to target " + targetId + ": " + e.getMessage());
throw new RuntimeException("Target attachment failed", e);
}
}
// Safe detachment
public void safeDetachFromTarget(SessionID sessionId, TargetID targetId) {
try {
devTools.send(domains.target().detachFromTarget(
Optional.of(sessionId),
Optional.of(targetId)
));
System.out.println("Successfully detached from target " + targetId);
} catch (Exception e) {
System.err.println("Failed to detach from target " + targetId + ": " + e.getMessage());
// Continue execution - detachment failures are often not critical
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-seleniumhq-selenium--selenium-devtools-v102