@weavetab/mcp 2.5.0-beta.5 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +49 -49
- package/ARCHITECTURE.md +590 -590
- package/README.md +216 -100
- package/SECURITY.md +207 -207
- package/TROUBLESHOOTING.md +373 -365
- package/dist/anticaptcha/detector.js +395 -395
- package/dist/anticaptcha/solver.js +130 -130
- package/dist/cdp/bridge.js +73 -73
- package/dist/cdp/connector.js +46 -22
- package/dist/cdp/extension.js +34 -34
- package/dist/cdp/helpers.js +174 -174
- package/dist/cdp/pruner.js +195 -195
- package/dist/cdp/ref_cache.js +66 -66
- package/dist/cdp/security.js +10 -10
- package/dist/cdp/verify.js +57 -57
- package/dist/cdp/walker.js +563 -525
- package/dist/cli/plugin.js +26 -3
- package/dist/config/loader.d.ts +0 -3
- package/dist/config/loader.js +0 -9
- package/dist/index.js +0 -4
- package/dist/intelligence/intent.js +81 -81
- package/dist/intelligence/sessionMemory.js +2 -2
- package/dist/overlay/ghost.js +22 -22
- package/dist/overlay/injector.js +89 -89
- package/dist/plugin/extension_bridge.js +54 -54
- package/dist/plugin/interface.d.ts +7 -0
- package/dist/plugin/loader.js +3 -4
- package/dist/plugin/schema.d.ts +11 -0
- package/dist/plugin/schema.js +59 -0
- package/dist/security/guard.js +18 -18
- package/dist/sensors/actionability.js +167 -167
- package/dist/sensors/guard.js +29 -29
- package/dist/sensors/network.js +15 -17
- package/dist/server.d.ts +1 -1
- package/dist/server.js +173 -144
- package/dist/state/delta.js +6 -7
- package/dist/tools/automation.js +85 -85
- package/dist/tools/canvas.js +48 -25
- package/dist/tools/checkpoint.js +94 -35
- package/dist/tools/click.js +52 -41
- package/dist/tools/clipboard.js +80 -74
- package/dist/tools/design_extract.js +441 -441
- package/dist/tools/dialog.js +13 -13
- package/dist/tools/emulation.d.ts +2 -0
- package/dist/tools/emulation.js +7 -0
- package/dist/tools/fill.js +344 -347
- package/dist/tools/find.js +78 -52
- package/dist/tools/highlight.js +67 -67
- package/dist/tools/inspect.js +54 -25
- package/dist/tools/key.js +42 -35
- package/dist/tools/navigate.d.ts +1 -1
- package/dist/tools/navigate.js +35 -35
- package/dist/tools/office/parse.js +62 -18
- package/dist/tools/pdf.d.ts +9 -0
- package/dist/tools/pdf.js +7 -4
- package/dist/tools/performance.d.ts +11 -4
- package/dist/tools/performance.js +94 -42
- package/dist/tools/performance_test.d.ts +2 -2
- package/dist/tools/performance_test.js +69 -69
- package/dist/tools/pointer.js +116 -116
- package/dist/tools/read.js +99 -67
- package/dist/tools/recording.js +9 -0
- package/dist/tools/scrape.js +146 -146
- package/dist/tools/screenshot.d.ts +2 -0
- package/dist/tools/screenshot.js +46 -4
- package/dist/tools/scroll.d.ts +3 -1
- package/dist/tools/scroll.js +13 -15
- package/dist/tools/select.d.ts +3 -0
- package/dist/tools/select.js +131 -124
- package/dist/tools/snapshot.js +8 -6
- package/dist/tools/storage.d.ts +5 -1
- package/dist/tools/storage.js +16 -0
- package/dist/tools/tabs.d.ts +7 -5
- package/dist/tools/tabs.js +79 -26
- package/dist/tools/thought.js +8 -8
- package/dist/tools/type.js +274 -274
- package/dist/tools/type_secret.js +23 -23
- package/dist/tools/upload.js +3 -3
- package/dist/tools/video_compiler.js +9 -0
- package/dist/tools/wait.js +35 -35
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/DOCUMENTATION.md +583 -585
- package/docs/PLUGINS.md +139 -121
- package/docs/TOOLS.md +3 -4
- package/docs/architecture/blind-injection.md +83 -83
- package/docs/architecture/overview.md +49 -49
- package/docs/configuration/configuration.md +39 -27
- package/docs/configuration/folders.md +62 -55
- package/docs/desktop-automation/electron-apps.md +225 -214
- package/docs/get-started/installation.md +1 -1
- package/docs/get-started/introduction.md +60 -60
- package/docs/get-started/quickstart.md +2 -2
- package/docs/get-started/setup-wizard.md +1 -1
- package/docs/intelligence/site-profiles.md +151 -151
- package/docs/tools/tools.md +582 -631
- package/docs/troubleshooting/trouble-loop.md +31 -31
- package/ext-dist/background.js +415 -415
- package/ext-dist/content.js +2527 -2527
- package/ext-dist/dashboard.html +583 -583
- package/ext-dist/hud/hud.js +3 -3
- package/ext-dist/hud/index.html +20 -20
- package/ext-dist/manifest.json +73 -73
- package/ext-dist/styles.css +223 -223
- package/mcp.json +796 -805
- package/package.json +8 -8
- package/server.json +23 -23
- package/smithery.yaml +7 -11
- package/dist/cdp/walker.test.d.ts +0 -2
- package/dist/cdp/walker.test.js +0 -172
- package/dist/intelligence/sessionMemory.test.d.ts +0 -2
- package/dist/intelligence/sessionMemory.test.js +0 -62
- package/dist/intelligence/trail.test.d.ts +0 -2
- package/dist/intelligence/trail.test.js +0 -110
- package/dist/state/cache.test.d.ts +0 -2
- package/dist/state/cache.test.js +0 -76
- package/dist/state/delta.test.d.ts +0 -2
- package/dist/state/delta.test.js +0 -279
- package/dist/state/groups.d.ts +0 -15
- package/dist/state/groups.js +0 -80
- package/dist/tools/accessibility.d.ts +0 -14
- package/dist/tools/accessibility.js +0 -32
- package/dist/tools/ask.d.ts +0 -14
- package/dist/tools/ask.js +0 -49
- package/dist/tools/click_and_wait.d.ts +0 -33
- package/dist/tools/click_and_wait.js +0 -149
- package/dist/tools/drag.d.ts +0 -15
- package/dist/tools/drag.js +0 -29
- package/dist/tools/geo.d.ts +0 -19
- package/dist/tools/geo.js +0 -29
- package/dist/tools/github/analyze.d.ts +0 -4
- package/dist/tools/github/analyze.js +0 -113
- package/dist/tools/github/cache.d.ts +0 -21
- package/dist/tools/github/cache.js +0 -28
- package/dist/tools/github/index.d.ts +0 -19
- package/dist/tools/github/index.js +0 -40
- package/dist/tools/github/issues.d.ts +0 -9
- package/dist/tools/github/issues.js +0 -88
- package/dist/tools/github/pr.d.ts +0 -5
- package/dist/tools/github/pr.js +0 -88
- package/dist/tools/github/rateLimiter.d.ts +0 -4
- package/dist/tools/github/rateLimiter.js +0 -46
- package/dist/tools/github/read.d.ts +0 -34
- package/dist/tools/github/read.js +0 -74
- package/dist/tools/github/repoContext.d.ts +0 -20
- package/dist/tools/github/repoContext.js +0 -129
- package/dist/tools/hover.d.ts +0 -19
- package/dist/tools/hover.js +0 -57
- package/dist/tools/integration.test.d.ts +0 -2
- package/dist/tools/integration.test.js +0 -96
- package/dist/tools/macro_compiler.d.ts +0 -7
- package/dist/tools/macro_compiler.js +0 -82
- package/dist/tools/mouse.d.ts +0 -21
- package/dist/tools/mouse.js +0 -62
- package/dist/tools/response.d.ts +0 -129
- package/dist/tools/response.js +0 -5
- package/dist/tools/wait_for.d.ts +0 -23
- package/dist/tools/wait_for.js +0 -30
- package/dist/tools/windows.d.ts +0 -22
- package/dist/tools/windows.js +0 -57
package/dist/tools/fill.js
CHANGED
|
@@ -35,49 +35,49 @@ function esc(s) {
|
|
|
35
35
|
*/
|
|
36
36
|
async function detectFieldType(session, selector) {
|
|
37
37
|
const { result } = await session.Runtime.evaluate({
|
|
38
|
-
expression: `(function() {
|
|
39
|
-
var el = document.querySelector(${esc(selector)});
|
|
40
|
-
if (!el) return JSON.stringify({ exists: false, fillable: false });
|
|
41
|
-
if (el.tagName === 'LABEL') {
|
|
42
|
-
var forId = el.getAttribute('for');
|
|
43
|
-
var target = forId ? document.getElementById(forId) : el.querySelector('input, textarea, select');
|
|
44
|
-
if (target) el = target;
|
|
45
|
-
} else if (!['input', 'select', 'textarea'].includes(el.tagName.toLowerCase()) && !el.isContentEditable) {
|
|
46
|
-
var inner = el.querySelector('input, textarea, select');
|
|
47
|
-
if (inner) el = inner;
|
|
48
|
-
}
|
|
49
|
-
var tag = el.tagName.toLowerCase();
|
|
50
|
-
var t = (el.getAttribute('type') || 'text').toLowerCase();
|
|
51
|
-
var role = (el.getAttribute('role') || '').toLowerCase();
|
|
52
|
-
var isCE = el.isContentEditable || el.getAttribute('contenteditable') === 'true' || el.getAttribute('contenteditable') === '';
|
|
53
|
-
var isCombobox = role === 'combobox' || role === 'textbox';
|
|
54
|
-
var type;
|
|
55
|
-
if (tag === 'select') type = 'select';
|
|
56
|
-
else if (tag === 'input' && t === 'checkbox') type = 'checkbox';
|
|
57
|
-
else if (tag === 'input' && t === 'radio') type = 'radio';
|
|
58
|
-
else if (role === 'checkbox') type = 'custom-checkbox';
|
|
59
|
-
else if (role === 'radio') type = 'custom-radio';
|
|
60
|
-
else if (role === 'switch') type = 'custom-checkbox';
|
|
61
|
-
else if (role === 'listbox' || role === 'option') type = 'select';
|
|
62
|
-
else if (tag === 'textarea' || (tag === 'input' && ['text','email','tel','url','password','search','number','time','date','datetime-local','month','week'].includes(t)) || isCE || isCombobox) type = 'text';
|
|
63
|
-
else type = 'unknown';
|
|
64
|
-
// Check fillability — disabled or readonly fields cannot be written to.
|
|
65
|
-
var ariaDisabled = el.getAttribute('aria-disabled');
|
|
66
|
-
var fillable = !el.disabled && !el.readOnly && ariaDisabled !== 'true' && ariaDisabled !== '';
|
|
67
|
-
// For checkboxes/radios, readOnly doesn't apply — but disabled does.
|
|
68
|
-
if (type === 'checkbox' || type === 'radio' || type === 'custom-checkbox' || type === 'custom-radio') {
|
|
69
|
-
fillable = !el.disabled && ariaDisabled !== 'true';
|
|
70
|
-
}
|
|
71
|
-
var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
|
|
72
|
-
var isSecret = el.hasAttribute('data-wt-secret') ||
|
|
73
|
-
(el.tagName === 'INPUT' && (
|
|
74
|
-
t === 'password' ||
|
|
75
|
-
secretRegex.test(el.getAttribute('autocomplete') || '') ||
|
|
76
|
-
secretRegex.test(el.getAttribute('name') || '') ||
|
|
77
|
-
secretRegex.test(el.getAttribute('id') || '') ||
|
|
78
|
-
secretRegex.test(el.getAttribute('aria-label') || '')
|
|
79
|
-
));
|
|
80
|
-
return JSON.stringify({ exists: true, type: type, fillable: fillable, isSecret: isSecret });
|
|
38
|
+
expression: `(function() {
|
|
39
|
+
var el = document.querySelector(${esc(selector)});
|
|
40
|
+
if (!el) return JSON.stringify({ exists: false, fillable: false });
|
|
41
|
+
if (el.tagName === 'LABEL') {
|
|
42
|
+
var forId = el.getAttribute('for');
|
|
43
|
+
var target = forId ? document.getElementById(forId) : el.querySelector('input, textarea, select, [contenteditable]:not([contenteditable="false"]), [data-slate-editor], [role="textbox"]');
|
|
44
|
+
if (target) el = target;
|
|
45
|
+
} else if (!['input', 'select', 'textarea'].includes(el.tagName.toLowerCase()) && !el.isContentEditable) {
|
|
46
|
+
var inner = el.querySelector('input, textarea, select, [contenteditable]:not([contenteditable="false"]), [data-slate-editor], [role="textbox"]');
|
|
47
|
+
if (inner) el = inner;
|
|
48
|
+
}
|
|
49
|
+
var tag = el.tagName.toLowerCase();
|
|
50
|
+
var t = (el.getAttribute('type') || 'text').toLowerCase();
|
|
51
|
+
var role = (el.getAttribute('role') || '').toLowerCase();
|
|
52
|
+
var isCE = el.isContentEditable || el.getAttribute('contenteditable') === 'true' || el.getAttribute('contenteditable') === '';
|
|
53
|
+
var isCombobox = role === 'combobox' || role === 'textbox';
|
|
54
|
+
var type;
|
|
55
|
+
if (tag === 'select') type = 'select';
|
|
56
|
+
else if (tag === 'input' && t === 'checkbox') type = 'checkbox';
|
|
57
|
+
else if (tag === 'input' && t === 'radio') type = 'radio';
|
|
58
|
+
else if (role === 'checkbox') type = 'custom-checkbox';
|
|
59
|
+
else if (role === 'radio') type = 'custom-radio';
|
|
60
|
+
else if (role === 'switch') type = 'custom-checkbox';
|
|
61
|
+
else if (role === 'listbox' || role === 'option') type = 'select';
|
|
62
|
+
else if (tag === 'textarea' || (tag === 'input' && ['text','email','tel','url','password','search','number','time','date','datetime-local','month','week'].includes(t)) || isCE || isCombobox) type = 'text';
|
|
63
|
+
else type = 'unknown';
|
|
64
|
+
// Check fillability — disabled or readonly fields cannot be written to.
|
|
65
|
+
var ariaDisabled = el.getAttribute('aria-disabled');
|
|
66
|
+
var fillable = !el.disabled && !el.readOnly && ariaDisabled !== 'true' && ariaDisabled !== '';
|
|
67
|
+
// For checkboxes/radios, readOnly doesn't apply — but disabled does.
|
|
68
|
+
if (type === 'checkbox' || type === 'radio' || type === 'custom-checkbox' || type === 'custom-radio') {
|
|
69
|
+
fillable = !el.disabled && ariaDisabled !== 'true';
|
|
70
|
+
}
|
|
71
|
+
var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
|
|
72
|
+
var isSecret = el.hasAttribute('data-wt-secret') ||
|
|
73
|
+
(el.tagName === 'INPUT' && (
|
|
74
|
+
t === 'password' ||
|
|
75
|
+
secretRegex.test(el.getAttribute('autocomplete') || '') ||
|
|
76
|
+
secretRegex.test(el.getAttribute('name') || '') ||
|
|
77
|
+
secretRegex.test(el.getAttribute('id') || '') ||
|
|
78
|
+
secretRegex.test(el.getAttribute('aria-label') || '')
|
|
79
|
+
));
|
|
80
|
+
return JSON.stringify({ exists: true, type: type, fillable: fillable, isSecret: isSecret });
|
|
81
81
|
})()`,
|
|
82
82
|
returnByValue: true,
|
|
83
83
|
});
|
|
@@ -89,44 +89,44 @@ async function detectFieldType(session, selector) {
|
|
|
89
89
|
*/
|
|
90
90
|
async function getFieldState(session, selector, type) {
|
|
91
91
|
const { result } = await session.Runtime.evaluate({
|
|
92
|
-
expression: `(function(type) {
|
|
93
|
-
var el = document.querySelector(${esc(selector)});
|
|
94
|
-
if (!el) return JSON.stringify({ exists: "false" });
|
|
95
|
-
if (el.tagName === 'LABEL') {
|
|
96
|
-
var forId = el.getAttribute('for');
|
|
97
|
-
var target = forId ? document.getElementById(forId) : el.querySelector('input, textarea, select');
|
|
98
|
-
if (target) el = target;
|
|
99
|
-
} else if (!['input', 'select', 'textarea'].includes(el.tagName.toLowerCase()) && !el.isContentEditable) {
|
|
100
|
-
var inner = el.querySelector('input, textarea, select');
|
|
101
|
-
if (inner) el = inner;
|
|
102
|
-
}
|
|
103
|
-
var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
|
|
104
|
-
var isSecret = el.hasAttribute('data-wt-secret') ||
|
|
105
|
-
(el.tagName === 'INPUT' && (
|
|
106
|
-
(el.type || '').toLowerCase() === 'password' ||
|
|
107
|
-
secretRegex.test(el.getAttribute('autocomplete') || '') ||
|
|
108
|
-
secretRegex.test(el.getAttribute('name') || '') ||
|
|
109
|
-
secretRegex.test(el.getAttribute('id') || '') ||
|
|
110
|
-
secretRegex.test(el.getAttribute('aria-label') || '')
|
|
111
|
-
));
|
|
112
|
-
var s = { exists: "true", isSecret: isSecret ? "true" : "false" };
|
|
113
|
-
if (el.tagName === 'SELECT') {
|
|
114
|
-
var idx = el.selectedIndex;
|
|
115
|
-
s.selectedIndex = String(idx);
|
|
116
|
-
s.value = isSecret ? '[REDACTED]' : el.value;
|
|
117
|
-
s.text = idx >= 0 ? el.options[idx].text : "";
|
|
118
|
-
} else if (el.type === 'checkbox' || el.type === 'radio' || type === 'custom-checkbox' || type === 'custom-radio') {
|
|
119
|
-
// Native checkbox/radio uses .checked; ARIA elements use aria-checked
|
|
120
|
-
var ariaChecked = el.getAttribute('aria-checked');
|
|
121
|
-
var isChecked = el.checked !== undefined ? el.checked : (ariaChecked === 'true' || ariaChecked === 'mixed');
|
|
122
|
-
s.checked = isChecked ? "true" : "false";
|
|
123
|
-
if (ariaChecked !== null) s.ariaChecked = ariaChecked;
|
|
124
|
-
} else if (el.isContentEditable) {
|
|
125
|
-
s.text = isSecret ? '[REDACTED]' : (el.textContent || "");
|
|
126
|
-
} else {
|
|
127
|
-
s.value = isSecret ? '[REDACTED]' : el.value;
|
|
128
|
-
}
|
|
129
|
-
return JSON.stringify(s);
|
|
92
|
+
expression: `(function(type) {
|
|
93
|
+
var el = document.querySelector(${esc(selector)});
|
|
94
|
+
if (!el) return JSON.stringify({ exists: "false" });
|
|
95
|
+
if (el.tagName === 'LABEL') {
|
|
96
|
+
var forId = el.getAttribute('for');
|
|
97
|
+
var target = forId ? document.getElementById(forId) : el.querySelector('input, textarea, select');
|
|
98
|
+
if (target) el = target;
|
|
99
|
+
} else if (!['input', 'select', 'textarea'].includes(el.tagName.toLowerCase()) && !el.isContentEditable) {
|
|
100
|
+
var inner = el.querySelector('input, textarea, select');
|
|
101
|
+
if (inner) el = inner;
|
|
102
|
+
}
|
|
103
|
+
var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
|
|
104
|
+
var isSecret = el.hasAttribute('data-wt-secret') ||
|
|
105
|
+
(el.tagName === 'INPUT' && (
|
|
106
|
+
(el.type || '').toLowerCase() === 'password' ||
|
|
107
|
+
secretRegex.test(el.getAttribute('autocomplete') || '') ||
|
|
108
|
+
secretRegex.test(el.getAttribute('name') || '') ||
|
|
109
|
+
secretRegex.test(el.getAttribute('id') || '') ||
|
|
110
|
+
secretRegex.test(el.getAttribute('aria-label') || '')
|
|
111
|
+
));
|
|
112
|
+
var s = { exists: "true", isSecret: isSecret ? "true" : "false" };
|
|
113
|
+
if (el.tagName === 'SELECT') {
|
|
114
|
+
var idx = el.selectedIndex;
|
|
115
|
+
s.selectedIndex = String(idx);
|
|
116
|
+
s.value = isSecret ? '[REDACTED]' : el.value;
|
|
117
|
+
s.text = idx >= 0 ? el.options[idx].text : "";
|
|
118
|
+
} else if (el.type === 'checkbox' || el.type === 'radio' || type === 'custom-checkbox' || type === 'custom-radio') {
|
|
119
|
+
// Native checkbox/radio uses .checked; ARIA elements use aria-checked
|
|
120
|
+
var ariaChecked = el.getAttribute('aria-checked');
|
|
121
|
+
var isChecked = el.checked !== undefined ? el.checked : (ariaChecked === 'true' || ariaChecked === 'mixed');
|
|
122
|
+
s.checked = isChecked ? "true" : "false";
|
|
123
|
+
if (ariaChecked !== null) s.ariaChecked = ariaChecked;
|
|
124
|
+
} else if (el.isContentEditable) {
|
|
125
|
+
s.text = isSecret ? '[REDACTED]' : (el.textContent || "");
|
|
126
|
+
} else {
|
|
127
|
+
s.value = isSecret ? '[REDACTED]' : el.value;
|
|
128
|
+
}
|
|
129
|
+
return JSON.stringify(s);
|
|
130
130
|
})(${esc(type)})`,
|
|
131
131
|
returnByValue: true,
|
|
132
132
|
});
|
|
@@ -138,105 +138,103 @@ async function getFieldState(session, selector, type) {
|
|
|
138
138
|
*/
|
|
139
139
|
async function applyFieldValue(session, selector, value, type) {
|
|
140
140
|
const { result } = await session.Runtime.evaluate({
|
|
141
|
-
expression: `(function(selector, value, type) {
|
|
142
|
-
try {
|
|
143
|
-
var el = document.querySelector(selector);
|
|
144
|
-
if (!el) return JSON.stringify({ ok: false, msg: 'Element not found' });
|
|
145
|
-
if (el.tagName === 'LABEL') {
|
|
146
|
-
var forId = el.getAttribute('for');
|
|
147
|
-
var target = forId ? document.getElementById(forId) : el.querySelector('input, textarea, select');
|
|
148
|
-
if (target) el = target;
|
|
149
|
-
} else if (!['input', 'select', 'textarea'].includes(el.tagName.toLowerCase()) && !el.isContentEditable) {
|
|
150
|
-
var inner = el.querySelector('input, textarea, select');
|
|
151
|
-
if (inner) el = inner;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (type === 'text') {
|
|
155
|
-
el.focus();
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
var
|
|
166
|
-
var
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
el.dispatchEvent(new
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
var
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
el.
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
el.
|
|
193
|
-
el.
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
el.
|
|
212
|
-
el.
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if (
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
el.dispatchEvent(new
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
return JSON.stringify({ ok: false, msg:
|
|
237
|
-
}
|
|
238
|
-
return JSON.stringify({ ok: false, msg: e.message || String(e) });
|
|
239
|
-
}
|
|
141
|
+
expression: `(function(selector, value, type) {
|
|
142
|
+
try {
|
|
143
|
+
var el = document.querySelector(selector);
|
|
144
|
+
if (!el) return JSON.stringify({ ok: false, msg: 'Element not found' });
|
|
145
|
+
if (el.tagName === 'LABEL') {
|
|
146
|
+
var forId = el.getAttribute('for');
|
|
147
|
+
var target = forId ? document.getElementById(forId) : el.querySelector('input, textarea, select, [contenteditable]:not([contenteditable="false"]), [data-slate-editor], [role="textbox"]');
|
|
148
|
+
if (target) el = target;
|
|
149
|
+
} else if (!['input', 'select', 'textarea'].includes(el.tagName.toLowerCase()) && !el.isContentEditable) {
|
|
150
|
+
var inner = el.querySelector('input, textarea, select, [contenteditable]:not([contenteditable="false"]), [data-slate-editor], [role="textbox"]');
|
|
151
|
+
if (inner) el = inner;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (type === 'text') {
|
|
155
|
+
el.focus();
|
|
156
|
+
if (el.isContentEditable || el.getAttribute('contenteditable') === 'true' || el.hasAttribute('data-slate-editor')) {
|
|
157
|
+
// contenteditable/Slate: use execCommand to preserve React/Slate virtual DOM tree
|
|
158
|
+
var isSlate = el.hasAttribute('data-slate-editor') || !!el.closest('[data-slate-editor]');
|
|
159
|
+
if (!isSlate) {
|
|
160
|
+
el.textContent = value;
|
|
161
|
+
}
|
|
162
|
+
try { document.execCommand('selectAll', false, null); document.execCommand('insertText', false, value); } catch(e2) {}
|
|
163
|
+
} else {
|
|
164
|
+
var tag = el.tagName.toLowerCase();
|
|
165
|
+
var proto = tag === 'textarea' ? window.HTMLTextAreaElement.prototype : window.HTMLInputElement.prototype;
|
|
166
|
+
var ns = Object.getOwnPropertyDescriptor(proto, 'value')?.set;
|
|
167
|
+
if (ns) ns.call(el, value); else el.value = value;
|
|
168
|
+
}
|
|
169
|
+
el.dispatchEvent(new InputEvent('input', { bubbles: true, data: value, inputType: 'insertText' }));
|
|
170
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
171
|
+
return JSON.stringify({ ok: true, msg: 'filled' });
|
|
172
|
+
}
|
|
173
|
+
if (type === 'checkbox' || type === 'custom-checkbox') {
|
|
174
|
+
var shouldCheck = value === 'true' || value === 'yes' || value === 'checked' || value === '1' || value === 'on';
|
|
175
|
+
var isNative = el.tagName === 'INPUT' && el.type === 'checkbox';
|
|
176
|
+
if (isNative) {
|
|
177
|
+
// A real click both toggles the box and fires trusted click/input/change
|
|
178
|
+
// events, keeping React/Vue controlled components in sync. Never set
|
|
179
|
+
// el.checked and then also dispatch a synthetic click: dispatching a
|
|
180
|
+
// click event runs the checkbox's default activation behavior and
|
|
181
|
+
// toggles it straight back, leaving the box unchanged.
|
|
182
|
+
if (el.checked !== shouldCheck) {
|
|
183
|
+
el.click();
|
|
184
|
+
}
|
|
185
|
+
} else {
|
|
186
|
+
// ARIA custom checkbox (role=checkbox/switch): toggle aria-checked and click
|
|
187
|
+
var ariaChecked = el.getAttribute('aria-checked');
|
|
188
|
+
var isCurrentlyChecked = ariaChecked === 'true';
|
|
189
|
+
if (isCurrentlyChecked !== shouldCheck) {
|
|
190
|
+
el.setAttribute('aria-checked', shouldCheck ? 'true' : 'false');
|
|
191
|
+
el.click();
|
|
192
|
+
el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
|
|
193
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return JSON.stringify({ ok: true, msg: shouldCheck ? 'checked' : 'unchecked' });
|
|
197
|
+
}
|
|
198
|
+
if (type === 'radio' || type === 'custom-radio') {
|
|
199
|
+
var isNativeRadio = el.tagName === 'INPUT' && el.type === 'radio';
|
|
200
|
+
if (isNativeRadio) {
|
|
201
|
+
if (!el.checked) {
|
|
202
|
+
var nativeRadioSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'checked')?.set;
|
|
203
|
+
if (nativeRadioSetter) nativeRadioSetter.call(el, true); else el.checked = true;
|
|
204
|
+
el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
|
|
205
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
206
|
+
}
|
|
207
|
+
} else {
|
|
208
|
+
if (el.getAttribute('aria-checked') !== 'true') {
|
|
209
|
+
el.setAttribute('aria-checked', 'true');
|
|
210
|
+
el.click();
|
|
211
|
+
el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
|
|
212
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return JSON.stringify({ ok: true, msg: 'selected' });
|
|
216
|
+
}
|
|
217
|
+
if (type === 'select') {
|
|
218
|
+
var foundIdx = -1;
|
|
219
|
+
for (var j = 0; j < el.options.length; j++) {
|
|
220
|
+
var opt = el.options[j];
|
|
221
|
+
if (opt.value === value || opt.text === value || opt.text.trim() === value.trim()) {
|
|
222
|
+
foundIdx = j; break;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (foundIdx === -1) return JSON.stringify({ ok: false, msg: 'Option not found: ' + value });
|
|
226
|
+
var nativeSelectSetter = Object.getOwnPropertyDescriptor(window.HTMLSelectElement.prototype, 'value')?.set;
|
|
227
|
+
if (nativeSelectSetter) nativeSelectSetter.call(el, el.options[foundIdx].value);
|
|
228
|
+
else el.selectedIndex = foundIdx;
|
|
229
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
230
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
231
|
+
el.options[foundIdx].dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
|
232
|
+
return JSON.stringify({ ok: true, msg: 'selected:' + el.options[foundIdx].text });
|
|
233
|
+
}
|
|
234
|
+
return JSON.stringify({ ok: false, msg: 'Unsupported field type: ' + type });
|
|
235
|
+
} catch(e) {
|
|
236
|
+
return JSON.stringify({ ok: false, msg: e.message || String(e) });
|
|
237
|
+
}
|
|
240
238
|
})(${esc(selector)}, ${esc(value)}, ${esc(type)})`,
|
|
241
239
|
returnByValue: true,
|
|
242
240
|
});
|
|
@@ -436,69 +434,70 @@ export async function browserFill(session, options, config) {
|
|
|
436
434
|
if (options.scan || options.inputs || (!options.fields || options.fields.length === 0)) {
|
|
437
435
|
try {
|
|
438
436
|
const scanRes = await session.Runtime.evaluate({
|
|
439
|
-
expression: `(function() {
|
|
440
|
-
const all = Array.from(document.querySelectorAll('input:not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="image"]), textarea, select, [role="textbox"], [role="searchbox"], [role="combobox"], [role="checkbox"], [role="radio"], [role="switch"], [contenteditable="
|
|
441
|
-
const activeEl = document.activeElement;
|
|
442
|
-
|
|
443
|
-
return JSON.stringify(all.map((el, i) => {
|
|
444
|
-
const rect = el.getBoundingClientRect();
|
|
445
|
-
const isVisible = rect.width > 0 && rect.height > 0 && rect.top < window.innerHeight && rect.bottom > 0;
|
|
446
|
-
const tag = el.tagName.toLowerCase();
|
|
447
|
-
const type = (el.getAttribute('type') || (tag === 'textarea' ? 'textarea' : tag === 'select' ? 'select' : 'text')).toLowerCase();
|
|
448
|
-
const role = el.getAttribute('role') || '';
|
|
449
|
-
|
|
450
|
-
let label = el.getAttribute('aria-label') || el.getAttribute('placeholder') || el.getAttribute('title') || '';
|
|
451
|
-
if (!label && el.id) {
|
|
452
|
-
const lbl = document.querySelector('label[for="' + el.id + '"]');
|
|
453
|
-
if (lbl) label = lbl.innerText.trim();
|
|
454
|
-
}
|
|
455
|
-
if (!label && el.closest('label')) {
|
|
456
|
-
label = el.closest('label').innerText.trim();
|
|
457
|
-
}
|
|
458
|
-
if (!label) {
|
|
459
|
-
label = el.getAttribute('name') || '';
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
var
|
|
463
|
-
var
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
secretRegex.test(el.getAttribute('
|
|
468
|
-
secretRegex.test(el.getAttribute('
|
|
469
|
-
secretRegex.test(el.getAttribute('
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
const
|
|
487
|
-
const
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
437
|
+
expression: `(function() {
|
|
438
|
+
const all = Array.from(document.querySelectorAll('input:not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="image"]), textarea, select, [role="textbox"], [role="searchbox"], [role="combobox"], [role="checkbox"], [role="radio"], [role="switch"], [contenteditable]:not([contenteditable="false"]), [data-slate-editor], [data-lexical-editor], .ProseMirror'));
|
|
439
|
+
const activeEl = document.activeElement;
|
|
440
|
+
|
|
441
|
+
return JSON.stringify(all.map((el, i) => {
|
|
442
|
+
const rect = el.getBoundingClientRect();
|
|
443
|
+
const isVisible = rect.width > 0 && rect.height > 0 && rect.top < window.innerHeight && rect.bottom > 0;
|
|
444
|
+
const tag = el.tagName.toLowerCase();
|
|
445
|
+
const type = (el.getAttribute('type') || (tag === 'textarea' ? 'textarea' : tag === 'select' ? 'select' : 'text')).toLowerCase();
|
|
446
|
+
const role = el.getAttribute('role') || '';
|
|
447
|
+
|
|
448
|
+
let label = el.getAttribute('aria-label') || el.getAttribute('placeholder') || el.getAttribute('title') || '';
|
|
449
|
+
if (!label && el.id) {
|
|
450
|
+
const lbl = document.querySelector('label[for="' + el.id + '"]');
|
|
451
|
+
if (lbl) label = lbl.innerText.trim();
|
|
452
|
+
}
|
|
453
|
+
if (!label && el.closest('label')) {
|
|
454
|
+
label = el.closest('label').innerText.trim();
|
|
455
|
+
}
|
|
456
|
+
if (!label) {
|
|
457
|
+
label = el.getAttribute('name') || '';
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
var isCheckable = type === 'checkbox' || type === 'radio' || role === 'checkbox' || role === 'radio' || role === 'switch';
|
|
461
|
+
var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
|
|
462
|
+
var isSecret = !isCheckable && (el.hasAttribute('data-wt-secret') ||
|
|
463
|
+
(el.tagName === 'INPUT' && (
|
|
464
|
+
type === 'password' ||
|
|
465
|
+
secretRegex.test(el.getAttribute('autocomplete') || '') ||
|
|
466
|
+
secretRegex.test(el.getAttribute('name') || '') ||
|
|
467
|
+
secretRegex.test(el.getAttribute('id') || '') ||
|
|
468
|
+
secretRegex.test(el.getAttribute('aria-label') || '')
|
|
469
|
+
)));
|
|
470
|
+
|
|
471
|
+
let val = '';
|
|
472
|
+
if (isCheckable) {
|
|
473
|
+
val = el.checked !== undefined ? String(el.checked) : (el.getAttribute('aria-checked') || 'false');
|
|
474
|
+
} else if (isSecret) {
|
|
475
|
+
val = '[REDACTED]';
|
|
476
|
+
} else if (tag === 'select') {
|
|
477
|
+
val = el.options && el.selectedIndex >= 0 && el.options[el.selectedIndex] ? el.options[el.selectedIndex].text : (el.value || '');
|
|
478
|
+
} else if (el.isContentEditable) {
|
|
479
|
+
val = (el.textContent || '').trim().slice(0, 40);
|
|
480
|
+
} else {
|
|
481
|
+
val = el.value !== undefined ? String(el.value).slice(0, 40) : '';
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const wtId = el.getAttribute('data-wt-id');
|
|
485
|
+
const ref = wtId ? ('w:' + wtId) : (el.id ? ('#' + el.id) : (el.name ? ('[name="' + el.name + '"]') : ('input:' + i)));
|
|
486
|
+
const isFocused = el === activeEl || (activeEl && el.contains(activeEl));
|
|
487
|
+
|
|
488
|
+
return {
|
|
489
|
+
ref,
|
|
490
|
+
tag,
|
|
491
|
+
type: role || type,
|
|
492
|
+
name: el.getAttribute('name') || undefined,
|
|
493
|
+
label: label.slice(0, 50) || undefined,
|
|
494
|
+
placeholder: el.getAttribute('placeholder') || undefined,
|
|
495
|
+
value: val,
|
|
496
|
+
focused: isFocused ? true : undefined,
|
|
497
|
+
disabled: (el.disabled || el.getAttribute('aria-disabled') === 'true') ? true : undefined,
|
|
498
|
+
visible: isVisible
|
|
499
|
+
};
|
|
500
|
+
}));
|
|
502
501
|
})()`,
|
|
503
502
|
returnByValue: true,
|
|
504
503
|
});
|
|
@@ -506,7 +505,7 @@ export async function browserFill(session, options, config) {
|
|
|
506
505
|
return {
|
|
507
506
|
success: true,
|
|
508
507
|
total_fields: inputsList.length,
|
|
509
|
-
succeeded:
|
|
508
|
+
succeeded: 0,
|
|
510
509
|
failed: 0,
|
|
511
510
|
submitted: false,
|
|
512
511
|
inputs: inputsList,
|
|
@@ -564,100 +563,98 @@ export async function browserFill(session, options, config) {
|
|
|
564
563
|
if (options.blast_mode) {
|
|
565
564
|
// blast_mode: single CDP round-trip filling all fields at once.
|
|
566
565
|
// Handles text, checkbox, radio, select, contenteditable, and ARIA variants.
|
|
567
|
-
const blastExpression = `(function(fields) {
|
|
568
|
-
var results = [];
|
|
569
|
-
var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
|
|
570
|
-
fields.forEach(function(f) {
|
|
571
|
-
var el = document.querySelector(f.selector);
|
|
572
|
-
if (!el) { results.push({ ref: f.ref, ok: false, msg: 'not found', type: 'unknown', isSecret: false }); return; }
|
|
573
|
-
var tag = el.tagName.toLowerCase();
|
|
574
|
-
var inputType = (el.getAttribute('type') || '').toLowerCase();
|
|
575
|
-
var role = (el.getAttribute('role') || '').toLowerCase();
|
|
576
|
-
var isSecret = el.hasAttribute('data-wt-secret') ||
|
|
577
|
-
(tag === 'input' && (
|
|
578
|
-
inputType === 'password' ||
|
|
579
|
-
secretRegex.test(el.getAttribute('autocomplete') || '') ||
|
|
580
|
-
secretRegex.test(el.getAttribute('name') || '') ||
|
|
581
|
-
secretRegex.test(el.getAttribute('id') || '') ||
|
|
582
|
-
secretRegex.test(el.getAttribute('aria-label') || '')
|
|
583
|
-
));
|
|
584
|
-
if (isSecret) {
|
|
585
|
-
try { el.setAttribute('data-wt-secret', 'true'); } catch(_) {}
|
|
586
|
-
}
|
|
587
|
-
try {
|
|
588
|
-
if (tag === 'select') {
|
|
589
|
-
// Select element
|
|
590
|
-
var foundIdx = -1;
|
|
591
|
-
for (var j = 0; j < el.options.length; j++) {
|
|
592
|
-
var opt = el.options[j];
|
|
593
|
-
if (opt.value === f.value || opt.text === f.value || opt.text.trim() === f.value.trim()) { foundIdx = j; break; }
|
|
594
|
-
}
|
|
595
|
-
if (foundIdx === -1) { results.push({ ref: f.ref, ok: false, msg: 'Option not found: ' + f.value, type: 'select', isSecret: isSecret }); return; }
|
|
596
|
-
var nativeSS = Object.getOwnPropertyDescriptor(window.HTMLSelectElement.prototype, 'value')?.set;
|
|
597
|
-
if (nativeSS) nativeSS.call(el, el.options[foundIdx].value); else el.selectedIndex = foundIdx;
|
|
598
|
-
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
599
|
-
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
600
|
-
results.push({ ref: f.ref, ok: true, msg: 'selected:' + el.options[foundIdx].text, type: 'select', isSecret: isSecret });
|
|
601
|
-
} else if (tag === 'input' && inputType === 'checkbox') {
|
|
602
|
-
// Native checkbox
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
el.
|
|
626
|
-
el.
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
el.
|
|
635
|
-
el.
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
el.
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
});
|
|
660
|
-
return JSON.stringify(results);
|
|
566
|
+
const blastExpression = `(function(fields) {
|
|
567
|
+
var results = [];
|
|
568
|
+
var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
|
|
569
|
+
fields.forEach(function(f) {
|
|
570
|
+
var el = document.querySelector(f.selector);
|
|
571
|
+
if (!el) { results.push({ ref: f.ref, ok: false, msg: 'not found', type: 'unknown', isSecret: false }); return; }
|
|
572
|
+
var tag = el.tagName.toLowerCase();
|
|
573
|
+
var inputType = (el.getAttribute('type') || '').toLowerCase();
|
|
574
|
+
var role = (el.getAttribute('role') || '').toLowerCase();
|
|
575
|
+
var isSecret = el.hasAttribute('data-wt-secret') ||
|
|
576
|
+
(tag === 'input' && (
|
|
577
|
+
inputType === 'password' ||
|
|
578
|
+
secretRegex.test(el.getAttribute('autocomplete') || '') ||
|
|
579
|
+
secretRegex.test(el.getAttribute('name') || '') ||
|
|
580
|
+
secretRegex.test(el.getAttribute('id') || '') ||
|
|
581
|
+
secretRegex.test(el.getAttribute('aria-label') || '')
|
|
582
|
+
));
|
|
583
|
+
if (isSecret) {
|
|
584
|
+
try { el.setAttribute('data-wt-secret', 'true'); } catch(_) {}
|
|
585
|
+
}
|
|
586
|
+
try {
|
|
587
|
+
if (tag === 'select') {
|
|
588
|
+
// Select element
|
|
589
|
+
var foundIdx = -1;
|
|
590
|
+
for (var j = 0; j < el.options.length; j++) {
|
|
591
|
+
var opt = el.options[j];
|
|
592
|
+
if (opt.value === f.value || opt.text === f.value || opt.text.trim() === f.value.trim()) { foundIdx = j; break; }
|
|
593
|
+
}
|
|
594
|
+
if (foundIdx === -1) { results.push({ ref: f.ref, ok: false, msg: 'Option not found: ' + f.value, type: 'select', isSecret: isSecret }); return; }
|
|
595
|
+
var nativeSS = Object.getOwnPropertyDescriptor(window.HTMLSelectElement.prototype, 'value')?.set;
|
|
596
|
+
if (nativeSS) nativeSS.call(el, el.options[foundIdx].value); else el.selectedIndex = foundIdx;
|
|
597
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
598
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
599
|
+
results.push({ ref: f.ref, ok: true, msg: 'selected:' + el.options[foundIdx].text, type: 'select', isSecret: isSecret });
|
|
600
|
+
} else if (tag === 'input' && inputType === 'checkbox') {
|
|
601
|
+
// Native checkbox — a real click toggles once and fires click/input/change
|
|
602
|
+
// (dispatching a synthetic click after setting el.checked toggles it back).
|
|
603
|
+
var want = f.value === 'true' || f.value === 'yes' || f.value === 'checked' || f.value === '1' || f.value === 'on';
|
|
604
|
+
if (el.checked !== want) {
|
|
605
|
+
el.click();
|
|
606
|
+
}
|
|
607
|
+
results.push({ ref: f.ref, ok: true, msg: want ? 'checked' : 'unchecked', type: 'checkbox', isSecret: isSecret });
|
|
608
|
+
} else if (tag === 'input' && inputType === 'radio') {
|
|
609
|
+
// Native radio
|
|
610
|
+
if (!el.checked) {
|
|
611
|
+
var nsr = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'checked')?.set;
|
|
612
|
+
if (nsr) nsr.call(el, true); else el.checked = true;
|
|
613
|
+
el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
|
|
614
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
615
|
+
}
|
|
616
|
+
results.push({ ref: f.ref, ok: true, msg: 'selected', type: 'radio', isSecret: isSecret });
|
|
617
|
+
} else if (role === 'checkbox' || role === 'switch') {
|
|
618
|
+
// ARIA custom checkbox
|
|
619
|
+
var wantAria = f.value === 'true' || f.value === 'yes' || f.value === 'checked' || f.value === '1' || f.value === 'on';
|
|
620
|
+
var curAria = el.getAttribute('aria-checked') === 'true';
|
|
621
|
+
if (curAria !== wantAria) {
|
|
622
|
+
el.setAttribute('aria-checked', wantAria ? 'true' : 'false');
|
|
623
|
+
el.click();
|
|
624
|
+
el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
|
|
625
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
626
|
+
}
|
|
627
|
+
results.push({ ref: f.ref, ok: true, msg: wantAria ? 'checked' : 'unchecked', type: 'custom-checkbox', isSecret: isSecret });
|
|
628
|
+
} else if (role === 'radio') {
|
|
629
|
+
// ARIA custom radio
|
|
630
|
+
if (el.getAttribute('aria-checked') !== 'true') {
|
|
631
|
+
el.setAttribute('aria-checked', 'true');
|
|
632
|
+
el.click();
|
|
633
|
+
el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
|
|
634
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
635
|
+
}
|
|
636
|
+
results.push({ ref: f.ref, ok: true, msg: 'selected', type: 'custom-radio', isSecret: isSecret });
|
|
637
|
+
} else if (el.isContentEditable || el.getAttribute('contenteditable') === 'true' || el.getAttribute('contenteditable') === '') {
|
|
638
|
+
// contenteditable
|
|
639
|
+
el.focus();
|
|
640
|
+
el.textContent = f.value;
|
|
641
|
+
try { document.execCommand('selectAll', false, null); document.execCommand('insertText', false, f.value); } catch(e2) {}
|
|
642
|
+
el.dispatchEvent(new InputEvent('input', { bubbles: true, data: f.value, inputType: 'insertText' }));
|
|
643
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
644
|
+
results.push({ ref: f.ref, ok: true, msg: 'filled', type: 'contenteditable', isSecret: isSecret });
|
|
645
|
+
} else {
|
|
646
|
+
// text / textarea / other inputs
|
|
647
|
+
el.focus();
|
|
648
|
+
var proto2 = tag === 'textarea' ? window.HTMLTextAreaElement.prototype : window.HTMLInputElement.prototype;
|
|
649
|
+
var ns2 = Object.getOwnPropertyDescriptor(proto2, 'value')?.set;
|
|
650
|
+
if (ns2) ns2.call(el, f.value); else el.value = f.value;
|
|
651
|
+
el.dispatchEvent(new InputEvent('input', { bubbles: true, data: f.value, inputType: 'insertText' }));
|
|
652
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
653
|
+
results.push({ ref: f.ref, ok: true, msg: 'filled', type: tag, isSecret: isSecret });
|
|
654
|
+
}
|
|
655
|
+
} catch(e) { results.push({ ref: f.ref, ok: false, msg: e.message || String(e), type: tag, isSecret: isSecret }); }
|
|
656
|
+
});
|
|
657
|
+
return JSON.stringify(results);
|
|
661
658
|
})(${JSON.stringify(options.fields.map(f => ({ ref: f.ref || f.selector || f.name || f.label || 'field', selector: toSelector(f), value: f.value })))})`;
|
|
662
659
|
const { result } = await session.Runtime.evaluate({ expression: blastExpression, returnByValue: true });
|
|
663
660
|
const blastResults = JSON.parse(result?.value || '[]');
|
|
@@ -793,15 +790,15 @@ export async function browserFill(session, options, config) {
|
|
|
793
790
|
if (options.submit && failed === 0) {
|
|
794
791
|
const subSelector = toSelector(options.submit);
|
|
795
792
|
const { result: subResult } = await session.Runtime.evaluate({
|
|
796
|
-
expression: `(function(sel) {
|
|
797
|
-
try {
|
|
798
|
-
var el = document.querySelector(sel);
|
|
799
|
-
if (!el) return 'not found';
|
|
800
|
-
el.focus();
|
|
801
|
-
el.click();
|
|
802
|
-
el.dispatchEvent(new Event('click', { bubbles: true }));
|
|
803
|
-
return 'clicked';
|
|
804
|
-
} catch(e) { return e.message; }
|
|
793
|
+
expression: `(function(sel) {
|
|
794
|
+
try {
|
|
795
|
+
var el = document.querySelector(sel);
|
|
796
|
+
if (!el) return 'not found';
|
|
797
|
+
el.focus();
|
|
798
|
+
el.click();
|
|
799
|
+
el.dispatchEvent(new Event('click', { bubbles: true }));
|
|
800
|
+
return 'clicked';
|
|
801
|
+
} catch(e) { return e.message; }
|
|
805
802
|
})(${esc(subSelector)})`,
|
|
806
803
|
returnByValue: true,
|
|
807
804
|
});
|