368 lines
15 KiB
JavaScript
368 lines
15 KiB
JavaScript
// ═══════════════════════════════════════════════════════════════════
|
|
// Deep Iframe Inspector for Antigravity
|
|
// ═══════════════════════════════════════════════════════════════════
|
|
// Usage: Paste this entire script into the AG Renderer DevTools console
|
|
// (Ctrl+Shift+I → Console tab in the main workbench window)
|
|
//
|
|
// This script recursively inspects ALL iframes, webview elements,
|
|
// and shadow DOMs to map the complete DOM tree including:
|
|
// - iframe nesting depth and origin info
|
|
// - webview elements and their executeJavaScript availability
|
|
// - shadow DOM roots
|
|
// - button inventory at each level
|
|
// - Content Security Policy headers
|
|
// - Cross-origin accessibility status
|
|
// ═══════════════════════════════════════════════════════════════════
|
|
|
|
(function deepIframeInspector() {
|
|
'use strict';
|
|
|
|
const SEP = '─'.repeat(70);
|
|
const results = [];
|
|
let nodeCount = 0;
|
|
|
|
function log(msg) {
|
|
console.log('[DEEP-INSPECT] ' + msg);
|
|
results.push(msg);
|
|
}
|
|
|
|
function indent(depth) {
|
|
return ' '.repeat(depth);
|
|
}
|
|
|
|
// ── Inspect a single document context ──
|
|
function inspectDocument(doc, depth, label) {
|
|
nodeCount++;
|
|
const id = `node_${nodeCount}`;
|
|
const pfx = indent(depth);
|
|
|
|
log(`${pfx}┌─ ${label} (depth=${depth})`);
|
|
|
|
if (!doc) {
|
|
log(`${pfx}│ ⛔ document is null/undefined`);
|
|
log(`${pfx}└─ END ${label}`);
|
|
return;
|
|
}
|
|
|
|
// Basic doc info
|
|
try {
|
|
log(`${pfx}│ URL: ${(doc.URL || doc.documentURI || 'unknown').substring(0, 120)}`);
|
|
log(`${pfx}│ title: "${(doc.title || '').substring(0, 80)}"`);
|
|
log(`${pfx}│ readyState: ${doc.readyState}`);
|
|
log(`${pfx}│ domain: ${doc.domain || 'N/A'}`);
|
|
} catch (e) {
|
|
log(`${pfx}│ ⛔ Cannot read doc properties: ${e.message}`);
|
|
}
|
|
|
|
// CSP meta tags
|
|
try {
|
|
const cspMetas = doc.querySelectorAll('meta[http-equiv="Content-Security-Policy"]');
|
|
if (cspMetas.length > 0) {
|
|
for (let i = 0; i < cspMetas.length; i++) {
|
|
log(`${pfx}│ CSP[${i}]: ${(cspMetas[i].content || '').substring(0, 150)}`);
|
|
}
|
|
}
|
|
} catch (e) {}
|
|
|
|
// Count key elements
|
|
try {
|
|
const allEls = doc.querySelectorAll('*');
|
|
const buttons = doc.querySelectorAll('button');
|
|
const inputs = doc.querySelectorAll('input[type="button"],input[type="submit"]');
|
|
const anchors = doc.querySelectorAll('a[role="button"]');
|
|
const iframes = doc.querySelectorAll('iframe');
|
|
const webviews = doc.querySelectorAll('webview');
|
|
const divWithRole = doc.querySelectorAll('[role="button"]');
|
|
|
|
log(`${pfx}│ Elements: total=${allEls.length} buttons=${buttons.length} roleBtn=${divWithRole.length} iframes=${iframes.length} webviews=${webviews.length}`);
|
|
|
|
// Dump ALL buttons (not just first 10)
|
|
if (buttons.length > 0) {
|
|
log(`${pfx}│ ── Buttons ──`);
|
|
const maxShow = Math.min(30, buttons.length);
|
|
for (let i = 0; i < maxShow; i++) {
|
|
const b = buttons[i];
|
|
const txt = (b.textContent || '').trim().substring(0, 60);
|
|
const cls = (b.className || '').substring(0, 50);
|
|
const disabled = b.disabled ? ' [DISABLED]' : '';
|
|
const hidden = (b.hidden || !b.offsetParent) ? ' [HIDDEN]' : '';
|
|
const ariaLabel = b.getAttribute('aria-label') || '';
|
|
const title = b.getAttribute('title') || '';
|
|
log(`${pfx}│ btn[${i}]: "${txt}"${disabled}${hidden} class="${cls}" aria="${ariaLabel}" title="${title}"`);
|
|
}
|
|
if (buttons.length > maxShow) {
|
|
log(`${pfx}│ ... +${buttons.length - maxShow} more buttons`);
|
|
}
|
|
}
|
|
|
|
// Dump role="button" elements that aren't actual buttons
|
|
if (divWithRole.length > 0) {
|
|
log(`${pfx}│ ── role="button" elements ──`);
|
|
const maxShow = Math.min(15, divWithRole.length);
|
|
for (let i = 0; i < maxShow; i++) {
|
|
const el = divWithRole[i];
|
|
if (el.tagName === 'BUTTON') continue; // skip actual buttons
|
|
const txt = (el.textContent || '').trim().substring(0, 60);
|
|
const tag = el.tagName.toLowerCase();
|
|
log(`${pfx}│ roleBtn[${i}]: <${tag}> "${txt}"`);
|
|
}
|
|
}
|
|
|
|
// Recurse into shadow DOMs
|
|
let shadowCount = 0;
|
|
for (let i = 0; i < allEls.length; i++) {
|
|
const sr = allEls[i].shadowRoot;
|
|
if (sr) {
|
|
shadowCount++;
|
|
const tag = allEls[i].tagName.toLowerCase();
|
|
const cls = (allEls[i].className || '').substring(0, 40);
|
|
inspectDocument(sr, depth + 1, `ShadowRoot of <${tag} class="${cls}">`);
|
|
}
|
|
}
|
|
if (shadowCount === 0) {
|
|
log(`${pfx}│ (no shadow DOMs found)`);
|
|
}
|
|
|
|
// Recurse into iframes
|
|
for (let i = 0; i < iframes.length; i++) {
|
|
const f = iframes[i];
|
|
const src = (f.src || '').substring(0, 120);
|
|
const cls = (f.className || '').substring(0, 50);
|
|
const id = f.id || '';
|
|
const sandbox = f.getAttribute('sandbox') || 'none';
|
|
const allow = f.getAttribute('allow') || '';
|
|
|
|
let fLabel = `<iframe#${i}> id="${id}" class="${cls}" src="${src}" sandbox="${sandbox}"`;
|
|
|
|
try {
|
|
const idoc = f.contentDocument || (f.contentWindow && f.contentWindow.document);
|
|
if (idoc) {
|
|
inspectDocument(idoc, depth + 1, fLabel + ' [ACCESSIBLE]');
|
|
} else {
|
|
log(`${pfx}│ ${fLabel} → contentDocument=null`);
|
|
// Try contentWindow info
|
|
try {
|
|
const cw = f.contentWindow;
|
|
log(`${pfx}│ contentWindow exists: ${!!cw}`);
|
|
if (cw) {
|
|
log(`${pfx}│ contentWindow.length (sub-frames): ${cw.length}`);
|
|
try { log(`${pfx}│ contentWindow.location: ${cw.location.href}`); } catch (e2) {
|
|
log(`${pfx}│ contentWindow.location: ⛔ ${e2.message.substring(0, 60)}`);
|
|
}
|
|
}
|
|
} catch (e2) {}
|
|
}
|
|
} catch (e) {
|
|
log(`${pfx}│ ${fLabel} → ⛔ BLOCKED: ${e.message.substring(0, 80)}`);
|
|
// Still try to get some info about the blocked iframe
|
|
try {
|
|
const cw = f.contentWindow;
|
|
log(`${pfx}│ contentWindow exists: ${!!cw}`);
|
|
if (cw) {
|
|
log(`${pfx}│ contentWindow.length (sub-frames): ${cw.length}`);
|
|
}
|
|
} catch (e2) {
|
|
log(`${pfx}│ contentWindow also blocked: ${e2.message.substring(0, 60)}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Recurse into webview elements
|
|
for (let i = 0; i < webviews.length; i++) {
|
|
const wv = webviews[i];
|
|
const src = (wv.src || '').substring(0, 120);
|
|
const cls = (wv.className || '').substring(0, 50);
|
|
const partition = wv.getAttribute('partition') || '';
|
|
const preload = wv.getAttribute('preload') || '';
|
|
const nodeInteg = wv.getAttribute('nodeintegration') || '';
|
|
const nodeIntegSub = wv.getAttribute('nodeintegrationinsubframes') || '';
|
|
const webpref = wv.getAttribute('webpreferences') || '';
|
|
|
|
log(`${pfx}│ ── <webview#${i}> ──`);
|
|
log(`${pfx}│ src: ${src}`);
|
|
log(`${pfx}│ class: ${cls}`);
|
|
log(`${pfx}│ partition: ${partition}`);
|
|
log(`${pfx}│ preload: ${preload}`);
|
|
log(`${pfx}│ nodeintegration: ${nodeInteg}`);
|
|
log(`${pfx}│ webpreferences: ${webpref}`);
|
|
|
|
// Try contentDocument
|
|
try {
|
|
const wdoc = wv.contentDocument;
|
|
if (wdoc) {
|
|
inspectDocument(wdoc, depth + 1, `<webview#${i}> contentDocument [ACCESSIBLE]`);
|
|
} else {
|
|
log(`${pfx}│ contentDocument: null`);
|
|
}
|
|
} catch (e) {
|
|
log(`${pfx}│ contentDocument: ⛔ ${e.message.substring(0, 60)}`);
|
|
}
|
|
|
|
// Check executeJavaScript availability
|
|
log(`${pfx}│ executeJavaScript: ${typeof wv.executeJavaScript}`);
|
|
if (typeof wv.executeJavaScript === 'function') {
|
|
log(`${pfx}│ 🔑 executeJavaScript IS AVAILABLE — attempting probe...`);
|
|
try {
|
|
wv.executeJavaScript(`
|
|
(function() {
|
|
var btns = document.querySelectorAll('button');
|
|
var allEls = document.querySelectorAll('*');
|
|
var iframes = document.querySelectorAll('iframe');
|
|
var webviews = document.querySelectorAll('webview');
|
|
var btnTexts = [];
|
|
for (var i = 0; i < btns.length; i++) {
|
|
var txt = (btns[i].textContent || '').trim();
|
|
var disabled = btns[i].disabled ? ' [DISABLED]' : '';
|
|
var hidden = (btns[i].hidden || !btns[i].offsetParent) ? ' [HIDDEN]' : '';
|
|
var cls = (btns[i].className || '').substring(0, 40);
|
|
btnTexts.push('"' + txt.substring(0, 50) + '"' + disabled + hidden + ' cls=' + cls);
|
|
}
|
|
// Also check for role=button
|
|
var roleBtns = document.querySelectorAll('[role="button"]');
|
|
var roleBtnTexts = [];
|
|
for (var j = 0; j < roleBtns.length; j++) {
|
|
if (roleBtns[j].tagName !== 'BUTTON') {
|
|
roleBtnTexts.push('<' + roleBtns[j].tagName.toLowerCase() + '> "' + (roleBtns[j].textContent || '').trim().substring(0, 40) + '"');
|
|
}
|
|
}
|
|
|
|
// Check for shadow DOMs
|
|
var shadowCount = 0;
|
|
for (var k = 0; k < allEls.length; k++) {
|
|
if (allEls[k].shadowRoot) shadowCount++;
|
|
}
|
|
|
|
return JSON.stringify({
|
|
url: document.URL,
|
|
title: document.title,
|
|
totalElements: allEls.length,
|
|
buttons: btns.length,
|
|
buttonTexts: btnTexts.slice(0, 30),
|
|
roleBtns: roleBtnTexts.slice(0, 15),
|
|
iframes: iframes.length,
|
|
webviews: webviews.length,
|
|
shadowDOMs: shadowCount
|
|
});
|
|
})()
|
|
`).then(function(result) {
|
|
try {
|
|
const data = JSON.parse(result);
|
|
console.log('[DEEP-INSPECT] 📦 webview#' + i + ' INTERNAL PROBE:');
|
|
console.log('[DEEP-INSPECT] URL: ' + data.url);
|
|
console.log('[DEEP-INSPECT] title: ' + data.title);
|
|
console.log('[DEEP-INSPECT] Elements: total=' + data.totalElements + ' buttons=' + data.buttons + ' iframes=' + data.iframes + ' webviews=' + data.webviews + ' shadowDOMs=' + data.shadowDOMs);
|
|
if (data.buttonTexts.length > 0) {
|
|
console.log('[DEEP-INSPECT] ── Buttons inside webview ──');
|
|
data.buttonTexts.forEach(function(t, idx) {
|
|
console.log('[DEEP-INSPECT] btn[' + idx + ']: ' + t);
|
|
});
|
|
}
|
|
if (data.roleBtns.length > 0) {
|
|
console.log('[DEEP-INSPECT] ── role="button" inside webview ──');
|
|
data.roleBtns.forEach(function(t, idx) {
|
|
console.log('[DEEP-INSPECT] roleBtn[' + idx + ']: ' + t);
|
|
});
|
|
}
|
|
} catch (e) {
|
|
console.log('[DEEP-INSPECT] raw result: ' + result);
|
|
}
|
|
}).catch(function(e) {
|
|
console.log('[DEEP-INSPECT] ⛔ executeJavaScript FAILED: ' + e.message);
|
|
});
|
|
} catch (e) {
|
|
log(`${pfx}│ ⛔ executeJavaScript call threw: ${e.message}`);
|
|
}
|
|
}
|
|
|
|
// Try getWebContentsId (Electron-specific)
|
|
try {
|
|
if (typeof wv.getWebContentsId === 'function') {
|
|
log(`${pfx}│ webContentsId: ${wv.getWebContentsId()}`);
|
|
}
|
|
} catch (e) {}
|
|
}
|
|
|
|
} catch (e) {
|
|
log(`${pfx}│ ⛔ Error during inspection: ${e.message}`);
|
|
}
|
|
|
|
log(`${pfx}└─ END ${label}`);
|
|
}
|
|
|
|
// ── Window hierarchy ──
|
|
function inspectWindowHierarchy() {
|
|
log(SEP);
|
|
log('📐 Window Hierarchy');
|
|
log(SEP);
|
|
log(`window.location.href: ${window.location.href}`);
|
|
log(`window.location.origin: ${window.location.origin}`);
|
|
log(`window.location.protocol: ${window.location.protocol}`);
|
|
log(`window.frames.length: ${window.frames.length}`);
|
|
log(`window === window.top: ${window === window.top}`);
|
|
log(`window === window.parent: ${window === window.parent}`);
|
|
log(`navigator.userAgent: ${navigator.userAgent.substring(0, 120)}`);
|
|
|
|
// Check if we're in Electron
|
|
try {
|
|
log(`process.type: ${typeof process !== 'undefined' ? process.type : 'N/A'}`);
|
|
log(`process.versions.electron: ${typeof process !== 'undefined' && process.versions ? process.versions.electron : 'N/A'}`);
|
|
} catch (e) {
|
|
log(`process info: N/A (${e.message.substring(0, 40)})`);
|
|
}
|
|
|
|
// Check webFrame (Electron renderer API)
|
|
try {
|
|
if (typeof require === 'function') {
|
|
const { webFrame } = require('electron');
|
|
log(`webFrame available: ${!!webFrame}`);
|
|
if (webFrame) {
|
|
log(`webFrame.routingId: ${webFrame.routingId}`);
|
|
}
|
|
}
|
|
} catch (e) {
|
|
log(`electron.webFrame: N/A (${e.message.substring(0, 40)})`);
|
|
}
|
|
|
|
// Check webContents access via remote/electron
|
|
try {
|
|
if (typeof require === 'function') {
|
|
const electron = require('electron');
|
|
const ipcRenderer = electron.ipcRenderer;
|
|
log(`ipcRenderer available: ${!!ipcRenderer}`);
|
|
}
|
|
} catch (e) {
|
|
log(`electron.ipcRenderer: N/A (${e.message.substring(0, 40)})`);
|
|
}
|
|
}
|
|
|
|
// ── Main ──
|
|
log('');
|
|
log('═'.repeat(70));
|
|
log(' DEEP IFRAME INSPECTOR — Antigravity DOM Analysis');
|
|
log(' Timestamp: ' + new Date().toISOString());
|
|
log('═'.repeat(70));
|
|
log('');
|
|
|
|
inspectWindowHierarchy();
|
|
|
|
log('');
|
|
log(SEP);
|
|
log('📄 Document Tree (recursive)');
|
|
log(SEP);
|
|
|
|
inspectDocument(document, 0, 'Main Document');
|
|
|
|
log('');
|
|
log(SEP);
|
|
log(`✅ Inspection complete. ${nodeCount} document contexts inspected.`);
|
|
log(SEP);
|
|
|
|
// Summary
|
|
log('');
|
|
log('📋 SUMMARY:');
|
|
log(' Copy all [DEEP-INSPECT] lines from this console');
|
|
log(' Webview executeJavaScript probe results will appear AFTER this summary (async)');
|
|
|
|
return results;
|
|
})();
|