diff --git a/src/background/main.js b/src/background/main.js index b4ad61a..3040b1f 100644 --- a/src/background/main.js +++ b/src/background/main.js @@ -546,10 +546,51 @@ async function onMessage(request, sender) { await resetCaptcha(sender.tab.id, sender.frameId, request.challengeUrl); } else if (request.id === 'getFramePos') { return getFramePos(sender.tab.id, sender.frameId, request.frameIndex); - } else if (request.id === 'getTabZoom') { - return browser.tabs.getZoom(sender.tab.id); - } else if (request.id === 'getBackgroundScriptScale') { - return window.devicePixelRatio; + } else if (request.id === 'getOsScale') { + let zoom = await browser.tabs.getZoom(sender.tab.id); + + const [[scale, windowWidth]] = await browser.tabs.executeScript( + sender.tab.id, + { + code: `[window.devicePixelRatio, window.innerWidth];`, + runAt: 'document_start' + } + ); + + if (targetEnv === 'firefox') { + // https://bugzilla.mozilla.org/show_bug.cgi?id=1787649 + + function getImageElement(url) { + return new Promise(resolve => { + const img = new Image(); + img.onload = () => { + resolve(img); + }; + img.onerror = () => { + resolve(); + }; + img.onabort = () => { + resolve(); + }; + img.src = url; + }); + } + + const screenshotWidth = ( + await getImageElement( + await browser.tabs.captureVisibleTab({ + format: 'jpeg', + quality: 10 + }) + ) + ).naturalWidth; + + if (Math.abs(screenshotWidth / windowWidth - scale * zoom) < 0.005) { + zoom = 1; + } + } + + return scale / zoom; } else if (request.id === 'startClientApp') { nativePort = browser.runtime.connectNative('org.buster.client'); } else if (request.id === 'stopClientApp') { diff --git a/src/content/initReset.js b/src/content/initReset.js index 891db99..281055b 100644 --- a/src/content/initReset.js +++ b/src/content/initReset.js @@ -6,7 +6,7 @@ function initReset(challengeUrl) { new CustomEvent('___resetCaptcha', {detail: challengeUrl}) ); }; - script.src = chrome.extension.getURL('/src/content/reset.js'); + script.src = chrome.runtime.getURL('/src/content/reset.js'); document.documentElement.appendChild(script); } diff --git a/src/content/setup.js b/src/content/setup.js index ccc28bd..14613e8 100644 --- a/src/content/setup.js +++ b/src/content/setup.js @@ -1,5 +1,5 @@ function setup() { - const url = new URL(chrome.extension.getURL('/src/setup/index.html')); + const url = new URL(chrome.runtime.getURL('/src/setup/index.html')); url.searchParams.set( 'session', new URL(window.location.href).searchParams.get('session') diff --git a/src/solve/main.js b/src/solve/main.js index 743e422..b51da9f 100644 --- a/src/solve/main.js +++ b/src/solve/main.js @@ -2,7 +2,13 @@ import browser from 'webextension-polyfill'; import storage from 'storage/storage'; import {meanSleep, pingClientApp} from 'utils/app'; -import {getText, waitForElement, getRandomFloat, sleep} from 'utils/common'; +import { + getText, + waitForElement, + getRandomFloat, + sleep, + getBrowser +} from 'utils/common'; import {targetEnv, clientAppVersion} from 'utils/config'; let solverWorking = false; @@ -51,13 +57,18 @@ function syncUI() { helpButton.remove(); const helpButtonHolder = document.querySelector('.help-button-holder'); - const shadow = helpButtonHolder.attachShadow({mode: 'closed'}); + helpButtonHolder.tabIndex = 2; + + const shadow = helpButtonHolder.attachShadow({ + mode: 'closed', + delegatesFocus: true + }); const link = document.createElement('link'); link.setAttribute('rel', 'stylesheet'); link.setAttribute( 'href', - browser.extension.getURL('/src/solve/solver-button.css') + browser.runtime.getURL('/src/solve/solver-button.css') ); shadow.appendChild(link); @@ -133,8 +144,8 @@ async function tapEnter(node, {navigateForward = true} = {}) { await messageClientApp({command: 'tapKey', data: 'enter'}); } -async function clickElement(node, browserBorder) { - const targetPos = await getClickPos(node, browserBorder); +async function clickElement(node, browserBorder, osScale) { + const targetPos = await getClickPos(node, browserBorder, osScale); await messageClientApp({command: 'moveMouse', ...targetPos}); await meanSleep(100); await messageClientApp({command: 'clickMouse'}); @@ -154,31 +165,31 @@ async function messageClientApp(message) { } async function getOsScale() { - // The background script devicePixelRatio is not affected by the default - // zoom level in Firefox, while the content script devicePixelRatio - // is affected, unless only text is zoomed. - if (targetEnv === 'firefox') { - return browser.runtime.sendMessage({id: 'getBackgroundScriptScale'}); - } - - const zoom = await browser.runtime.sendMessage({id: 'getTabZoom'}); - - return window.devicePixelRatio / zoom; + return browser.runtime.sendMessage({id: 'getOsScale'}); } -async function getBrowserBorder(clickEvent) { +async function getBrowserBorder(clickEvent, osScale) { const framePos = await getFrameClientPos(); const scale = window.devicePixelRatio; - const osScale = await getOsScale(); + + let evScreenPropScale = osScale; + if ( + targetEnv === 'firefox' && + parseInt((await getBrowser()).version.split('.')[0], 10) >= 99 + ) { + // https://bugzilla.mozilla.org/show_bug.cgi?id=1753836 + + evScreenPropScale = scale; + } return { left: - clickEvent.screenX * osScale - + clickEvent.screenX * evScreenPropScale - clickEvent.clientX * scale - framePos.x - window.screenX * scale, top: - clickEvent.screenY * osScale - + clickEvent.screenY * evScreenPropScale - clickEvent.clientY * scale - framePos.y - window.screenY * scale @@ -202,7 +213,7 @@ async function getFrameClientPos() { return {x: 0, y: 0}; } -async function getElementScreenRect(node, browserBorder) { +async function getElementScreenRect(node, browserBorder, osScale) { let {left: x, top: y, width, height} = node.getBoundingClientRect(); const data = await getFrameClientPos(); @@ -218,7 +229,6 @@ async function getElementScreenRect(node, browserBorder) { const {os} = await browser.runtime.sendMessage({id: 'getPlatform'}); if (['windows', 'macos'].includes(os)) { - const osScale = await getOsScale(); x /= osScale; y /= osScale; width /= osScale; @@ -228,8 +238,12 @@ async function getElementScreenRect(node, browserBorder) { return {x, y, width, height}; } -async function getClickPos(node, browserBorder) { - let {x, y, width, height} = await getElementScreenRect(node, browserBorder); +async function getClickPos(node, browserBorder, osScale) { + let {x, y, width, height} = await getElementScreenRect( + node, + browserBorder, + osScale + ); return { x: Math.round(x + width * getRandomFloat(0.4, 0.6)), @@ -248,10 +262,12 @@ async function solve(simulateUserInput, clickEvent) { ); let browserBorder; + let osScale; let useMouse = true; if (simulateUserInput) { if (!navigateWithKeyboard && (clickEvent.clientX || clickEvent.clientY)) { - browserBorder = await getBrowserBorder(clickEvent); + osScale = await getOsScale(); + browserBorder = await getBrowserBorder(clickEvent, osScale); } else { useMouse = false; } @@ -263,9 +279,10 @@ async function solve(simulateUserInput, clickEvent) { const audioButton = document.querySelector('#recaptcha-audio-button'); if (simulateUserInput) { if (useMouse) { - await clickElement(audioButton, browserBorder); + await clickElement(audioButton, browserBorder, osScale); } else { - await tapEnter(audioButton, {navigateForward: false}); + audioButton.focus(); + await tapEnter(audioButton); } } else { dispatchEnter(audioButton); @@ -325,7 +342,7 @@ async function solve(simulateUserInput, clickEvent) { '.rc-audiochallenge-play-button > button' ); if (useMouse) { - await clickElement(playButton, browserBorder); + await clickElement(playButton, browserBorder, osScale); } else { await tapEnter(playButton); } @@ -347,7 +364,7 @@ async function solve(simulateUserInput, clickEvent) { const input = document.querySelector('#audio-response'); if (simulateUserInput) { if (useMouse) { - await clickElement(input, browserBorder); + await clickElement(input, browserBorder, osScale); } else { await navigateToElement(input); } @@ -361,7 +378,7 @@ async function solve(simulateUserInput, clickEvent) { const submitButton = document.querySelector('#recaptcha-verify-button'); if (simulateUserInput) { if (useMouse) { - await clickElement(submitButton, browserBorder); + await clickElement(submitButton, browserBorder, osScale); } else { await tapEnter(submitButton); } diff --git a/src/solve/solver-button.css b/src/solve/solver-button.css index 710f5c6..8a04864 100644 --- a/src/solve/solver-button.css +++ b/src/solve/solver-button.css @@ -21,6 +21,10 @@ outline: none; } +#solver-button:focus-visible { + background-color: rgba(216, 216, 216, 0.8); +} + #solver-button.working { background-image: url('data:image/svg+xml,%3Csvg%20width%3D%2244%22%20height%3D%2244%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20stroke%3D%22%23727272%22%3E%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%20stroke-width%3D%225%22%3E%3Ccircle%20cx%3D%2222%22%20cy%3D%2222%22%20r%3D%221%22%3E%3Canimate%20attributeName%3D%22r%22%20begin%3D%220s%22%20dur%3D%221.8s%22%20values%3D%221%3B%2020%22%20calcMode%3D%22spline%22%20keyTimes%3D%220%3B%201%22%20keySplines%3D%220.165%2C%200.84%2C%200.44%2C%201%22%20repeatCount%3D%22indefinite%22%2F%3E%3Canimate%20attributeName%3D%22stroke-opacity%22%20begin%3D%220s%22%20dur%3D%221.8s%22%20values%3D%221%3B%200%22%20calcMode%3D%22spline%22%20keyTimes%3D%220%3B%201%22%20keySplines%3D%220.3%2C%200.61%2C%200.355%2C%201%22%20repeatCount%3D%22indefinite%22%2F%3E%3C%2Fcircle%3E%3Ccircle%20cx%3D%2222%22%20cy%3D%2222%22%20r%3D%221%22%3E%3Canimate%20attributeName%3D%22r%22%20begin%3D%22-0.9s%22%20dur%3D%221.8s%22%20values%3D%221%3B%2020%22%20calcMode%3D%22spline%22%20keyTimes%3D%220%3B%201%22%20keySplines%3D%220.165%2C%200.84%2C%200.44%2C%201%22%20repeatCount%3D%22indefinite%22%2F%3E%3Canimate%20attributeName%3D%22stroke-opacity%22%20begin%3D%22-0.9s%22%20dur%3D%221.8s%22%20values%3D%221%3B%200%22%20calcMode%3D%22spline%22%20keyTimes%3D%220%3B%201%22%20keySplines%3D%220.3%2C%200.61%2C%200.355%2C%201%22%20repeatCount%3D%22indefinite%22%2F%3E%3C%2Fcircle%3E%3C%2Fg%3E%3C%2Fsvg%3E') !important; opacity: 1; diff --git a/src/utils/app.js b/src/utils/app.js index f3501f9..36f6b51 100755 --- a/src/utils/app.js +++ b/src/utils/app.js @@ -57,7 +57,7 @@ function getOptionLabels(data, scope = 'optionValue') { async function showContributePage(action = false) { const activeTab = await getActiveTab(); - let url = browser.extension.getURL('/src/contribute/index.html'); + let url = browser.runtime.getURL('/src/contribute/index.html'); if (action) { url = `${url}?action=${action}`; }