# Advanced Chrome DevTools Protocol Techniques Advanced patterns for complex browser automation scenarios. ## Network Interception ### Monitor Network Requests ```json // Get all network requests via Performance API {action: "eval", payload: ` performance.getEntriesByType('resource').map(r => ({ name: r.name, type: r.initiatorType, duration: r.duration, size: r.transferSize })) `} ``` ### Wait for Specific Request ```json // Wait for API call to complete {action: "eval", payload: ` new Promise(resolve => { const check = () => { const apiCall = performance.getEntriesByType('resource') .find(r => r.name.includes('/api/data')); if (apiCall) { resolve(apiCall); } else { setTimeout(check, 100); } }; check(); }) `} ``` ### Check Response Status ```json // Fetch API to check endpoint {action: "eval", payload: ` fetch('https://api.example.com/status') .then(r => ({ status: r.status, ok: r.ok })) `} ``` --- ## JavaScript Injection ### Add Helper Functions ```json // Inject utility functions into page {action: "eval", payload: ` window.waitForElement = (selector, timeout = 5000) => { return new Promise((resolve, reject) => { const startTime = Date.now(); const check = () => { const elem = document.querySelector(selector); if (elem) { resolve(elem); } else if (Date.now() - startTime > timeout) { reject(new Error('Timeout')); } else { setTimeout(check, 100); } }; check(); }); }; 'Helper injected' `} // Use injected helper {action: "eval", payload: "window.waitForElement('.lazy-content')"} ``` ### Modify Page Behavior ```json // Disable animations for faster testing {action: "eval", payload: ` const style = document.createElement('style'); style.textContent = '* { animation: none !important; transition: none !important; }'; document.head.appendChild(style); 'Animations disabled' `} // Override fetch to log requests {action: "eval", payload: ` const originalFetch = window.fetch; window.fetch = function(...args) { console.log('Fetch:', args[0]); return originalFetch.apply(this, arguments); }; 'Fetch override installed' `} ``` --- ## Complex Waiting Patterns ### Wait for Multiple Conditions ```json {action: "eval", payload: ` Promise.all([ new Promise(r => { const check = () => document.querySelector('.element1') ? r() : setTimeout(check, 100); check(); }), new Promise(r => { const check = () => document.querySelector('.element2') ? r() : setTimeout(check, 100); check(); }) ]) `} ``` ### Wait with Mutation Observer ```json {action: "eval", payload: ` new Promise(resolve => { const observer = new MutationObserver((mutations) => { const target = document.querySelector('.dynamic-content'); if (target && target.textContent.trim() !== '') { observer.disconnect(); resolve(target.textContent); } }); observer.observe(document.body, { childList: true, subtree: true, characterData: true }); }) `} ``` ### Wait for Idle State ```json // Wait for network idle {action: "eval", payload: ` new Promise(resolve => { let lastActivity = Date.now(); // Monitor network activity const originalFetch = window.fetch; window.fetch = function(...args) { lastActivity = Date.now(); return originalFetch.apply(this, arguments); }; // Check if idle for 500ms const checkIdle = () => { if (Date.now() - lastActivity > 500) { window.fetch = originalFetch; resolve('idle'); } else { setTimeout(checkIdle, 100); } }; setTimeout(checkIdle, 100); }) `} ``` --- ## Data Manipulation ### Parse and Transform Table ```json {action: "eval", payload: ` (() => { const table = document.querySelector('table'); const headers = Array.from(table.querySelectorAll('thead th')) .map(th => th.textContent.trim()); const rows = Array.from(table.querySelectorAll('tbody tr')) .map(tr => { const cells = Array.from(tr.cells).map(td => td.textContent.trim()); return Object.fromEntries(headers.map((h, i) => [h, cells[i]])); }); // Filter and transform return rows .filter(row => parseFloat(row['Price'].replace('$', '')) > 100) .map(row => ({ ...row, priceNum: parseFloat(row['Price'].replace('$', '')) })) .sort((a, b) => b.priceNum - a.priceNum); })() `} ``` ### Extract Nested JSON from Script Tags ```json {action: "eval", payload: ` (() => { const scripts = Array.from(document.querySelectorAll('script[type="application/ld+json"]')); return scripts.map(s => JSON.parse(s.textContent)); })() `} ``` ### Aggregate Multiple Elements ```json {action: "eval", payload: ` (() => { const sections = Array.from(document.querySelectorAll('section.category')); return sections.map(section => ({ category: section.querySelector('h2').textContent, items: Array.from(section.querySelectorAll('.item')).map(item => ({ name: item.querySelector('.name').textContent, price: item.querySelector('.price').textContent, inStock: !item.querySelector('.out-of-stock') })), total: section.querySelectorAll('.item').length })); })() `} ``` --- ## State Management ### Save and Restore Form State ```json // Save form state {action: "eval", payload: ` (() => { const form = document.querySelector('form'); const data = {}; new FormData(form).forEach((value, key) => data[key] = value); localStorage.setItem('formBackup', JSON.stringify(data)); return data; })() `} // Restore form state {action: "eval", payload: ` (() => { const data = JSON.parse(localStorage.getItem('formBackup')); const form = document.querySelector('form'); Object.entries(data).forEach(([name, value]) => { const input = form.querySelector(\`[name="\${name}"]\`); if (input) input.value = value; }); return 'Form restored'; })() `} ``` ### Session Management ```json // Save session state {action: "eval", payload: ` ({ cookies: document.cookie, localStorage: JSON.stringify(localStorage), sessionStorage: JSON.stringify(sessionStorage), url: window.location.href }) `} // Restore session (on new page load) {action: "eval", payload: ` (() => { const session = {/* saved session data */}; // Restore cookies session.cookies.split('; ').forEach(cookie => { document.cookie = cookie; }); // Restore localStorage Object.entries(JSON.parse(session.localStorage)).forEach(([k, v]) => { localStorage.setItem(k, v); }); return 'Session restored'; })() `} ``` --- ## Visual Testing ### Check Element Visibility ```json {action: "eval", payload: ` (selector) => { const elem = document.querySelector(selector); if (!elem) return { visible: false, reason: 'not found' }; const rect = elem.getBoundingClientRect(); const style = window.getComputedStyle(elem); return { visible: rect.width > 0 && rect.height > 0 && style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0', rect: rect, computed: { display: style.display, visibility: style.visibility, opacity: style.opacity } }; } `} ``` ### Get Element Colors ```json {action: "eval", payload: ` (() => { const elem = document.querySelector('.button'); const style = window.getComputedStyle(elem); return { backgroundColor: style.backgroundColor, color: style.color, borderColor: style.borderColor }; })() `} ``` ### Measure Element Positions ```json {action: "eval", payload: ` (() => { const elements = Array.from(document.querySelectorAll('.item')); return elements.map(elem => { const rect = elem.getBoundingClientRect(); return { id: elem.id, x: rect.x, y: rect.y, width: rect.width, height: rect.height, inViewport: rect.top >= 0 && rect.bottom <= window.innerHeight }; }); })() `} ``` --- ## Performance Monitoring ### Get Page Load Metrics ```json {action: "eval", payload: ` (() => { const nav = performance.getEntriesByType('navigation')[0]; const paint = performance.getEntriesByType('paint'); return { dns: nav.domainLookupEnd - nav.domainLookupStart, tcp: nav.connectEnd - nav.connectStart, request: nav.responseStart - nav.requestStart, response: nav.responseEnd - nav.responseStart, domLoad: nav.domContentLoadedEventEnd - nav.domContentLoadedEventStart, pageLoad: nav.loadEventEnd - nav.loadEventStart, firstPaint: paint.find(p => p.name === 'first-paint')?.startTime, firstContentfulPaint: paint.find(p => p.name === 'first-contentful-paint')?.startTime }; })() `} ``` ### Monitor Memory Usage ```json {action: "eval", payload: ` performance.memory ? { usedJSHeapSize: performance.memory.usedJSHeapSize, totalJSHeapSize: performance.memory.totalJSHeapSize, jsHeapSizeLimit: performance.memory.jsHeapSizeLimit } : 'Memory API not available' `} ``` ### Get Resource Timing ```json {action: "eval", payload: ` (() => { const resources = performance.getEntriesByType('resource'); // Group by type const byType = {}; resources.forEach(r => { if (!byType[r.initiatorType]) byType[r.initiatorType] = []; byType[r.initiatorType].push({ name: r.name, duration: r.duration, size: r.transferSize }); }); return { total: resources.length, byType: Object.fromEntries( Object.entries(byType).map(([type, items]) => [ type, { count: items.length, totalDuration: items.reduce((sum, i) => sum + i.duration, 0), totalSize: items.reduce((sum, i) => sum + i.size, 0) } ]) ) }; })() `} ``` --- ## Accessibility Testing ### Check ARIA Labels ```json {action: "eval", payload: ` Array.from(document.querySelectorAll('button, a, input')).map(elem => ({ tag: elem.tagName, text: elem.textContent.trim(), ariaLabel: elem.getAttribute('aria-label'), ariaDescribedBy: elem.getAttribute('aria-describedby'), title: elem.getAttribute('title'), hasAccessibleName: !!(elem.getAttribute('aria-label') || elem.textContent.trim() || elem.getAttribute('title')) })) `} ``` ### Find Focus Order ```json {action: "eval", payload: ` Array.from(document.querySelectorAll('a, button, input, select, textarea, [tabindex]')) .filter(elem => { const style = window.getComputedStyle(elem); return style.display !== 'none' && style.visibility !== 'hidden'; }) .map((elem, index) => ({ index: index, tag: elem.tagName, tabIndex: elem.tabIndex, text: elem.textContent.trim().substring(0, 50) })) `} ``` --- ## Frame Handling ### List Frames ```json {action: "eval", payload: ` Array.from(document.querySelectorAll('iframe, frame')).map((frame, i) => ({ index: i, src: frame.src, name: frame.name, id: frame.id })) `} ``` ### Access Frame Content Note: Cross-origin frames cannot be accessed due to security restrictions. ```json // For same-origin frames only {action: "eval", payload: ` (() => { const frame = document.querySelector('iframe'); try { return { title: frame.contentDocument.title, body: frame.contentDocument.body.textContent.substring(0, 100) }; } catch (e) { return { error: 'Cross-origin frame - cannot access' }; } })() `} ``` --- ## Custom Events ### Trigger Custom Events ```json {action: "eval", payload: ` (() => { const event = new CustomEvent('myCustomEvent', { detail: { message: 'Hello from automation' } }); document.dispatchEvent(event); return 'Event dispatched'; })() `} ``` ### Listen for Events ```json {action: "eval", payload: ` new Promise(resolve => { const handler = (e) => { document.removeEventListener('myCustomEvent', handler); resolve(e.detail); }; document.addEventListener('myCustomEvent', handler); // Timeout after 5 seconds setTimeout(() => { document.removeEventListener('myCustomEvent', handler); resolve({ timeout: true }); }, 5000); }) `} ``` --- ## Browser Detection ### Get Browser Info ```json {action: "eval", payload: ` ({ userAgent: navigator.userAgent, platform: navigator.platform, language: navigator.language, cookiesEnabled: navigator.cookieEnabled, doNotTrack: navigator.doNotTrack, viewport: { width: window.innerWidth, height: window.innerHeight }, screen: { width: screen.width, height: screen.height, colorDepth: screen.colorDepth } }) `} ``` --- ## Testing Helpers ### Get All Interactive Elements ```json {action: "eval", payload: ` Array.from(document.querySelectorAll('a, button, input, select, textarea, [onclick], [role=button]')) .filter(elem => { const style = window.getComputedStyle(elem); return style.display !== 'none' && style.visibility !== 'hidden'; }) .map(elem => ({ tag: elem.tagName, type: elem.type, id: elem.id, class: elem.className, text: elem.textContent.trim().substring(0, 50), selector: elem.id ? \`#\${elem.id}\` : \`\${elem.tagName.toLowerCase()}\${elem.className ? '.' + elem.className.split(' ').join('.') : ''}\` })) `} ``` ### Validate Forms ```json {action: "eval", payload: ` (() => { const forms = Array.from(document.querySelectorAll('form')); return forms.map(form => ({ id: form.id, action: form.action, method: form.method, fields: Array.from(form.elements).map(elem => ({ name: elem.name, type: elem.type, required: elem.required, value: elem.value, valid: elem.checkValidity() })) })); })() `} ``` --- ## Debugging Tools ### Log Element Path ```json {action: "eval", payload: ` (selector) => { const elem = document.querySelector(selector); if (!elem) return null; const path = []; let current = elem; while (current && current !== document.body) { let selector = current.tagName.toLowerCase(); if (current.id) selector += '#' + current.id; if (current.className) selector += '.' + current.className.split(' ').join('.'); path.unshift(selector); current = current.parentElement; } return path.join(' > '); } `} ``` ### Find Element by Text ```json {action: "eval", payload: ` (text) => { const elements = Array.from(document.querySelectorAll('*')); const matches = elements.filter(elem => elem.textContent.includes(text) && !Array.from(elem.children).some(child => child.textContent.includes(text)) ); return matches.map(elem => ({ tag: elem.tagName, id: elem.id, class: elem.className, text: elem.textContent.trim().substring(0, 100) })); } `} ```