Предположим, я пишу сценарий букмарклета для изменения веб-страницы. Большинство веб-сайтов (использующих веб-пакет и т. д.) имеют примерно такую структуру:
<html>
<script type = "text/javascript">
const buttonClicked = (() => {
let internal_variable = 0;
const _internal_buttonClicked = () => {
internal_variable++;
document.getElementById("myButton").innerText = `Clicked ${internal_variable} times!`;
};
return _internal_buttonClicked.bind(this);
})();
</script>
<body>
<button id = "myButton" onclick = "buttonClicked()">Clicked 0 times!</button>
</body>
</html>
Я хотел бы получить доступ к internal_variable
и установить для него значение -100. С отладчиком это просто; Я делаю точку останова внутри _internal_buttonClicked()
, а затем меняю internal_variable
напрямую.
Как я могу сделать это без
Я знаю, что простой ответ — «вы не можете намеренно», но я готов принять любые обходные решения, решения, включающие расширения браузера/привилегированное выполнение, решения, специфичные для require
и т. д.
Связанный:
Я поощряю это, хотя знаю, что хорошего решения может и не быть. Я чувствую, что есть много качественных ответов о том, как отлаживать, исправлять или изменять двоичный файл, и не так уж много информации о том, как сделать то же самое с JS, и я надеюсь узнать, как подойти к этому чрезвычайно -Общая ситуация немного лучше. Если решения действительно нет, то документирование причин (я полагаю, есть проблемы с XSS?) будет следующим лучшим решением.
Если все ссылки на эту функцию находятся в атрибутах HTML onclick
, просто определите новую функцию и вместо этого сделайте ссылку на нее в этих атрибутах. Будет ли этого достаточно?
Ну, «решения, включающие расширения браузера/привилегированное выполнение» — это совершенно другой пример, чем «букмарклет». Посмотрите chrome.debugger/протокол удаленной отладки.
Этот вопрос касается именно встроенных скриптов?
Невозможно присвоить новое значение internal_variable
, поскольку оно не входит в область действия вашего кода. Вы также не можете назначить новую функцию buttonClicked
, поскольку она является константой.
Но в зависимости от реальной ситуации вы можете использовать другие подходы. Вот некоторые из них, которые подойдут для вашего примера:
innerText
Попросите его изменить число, отображаемое на кнопке, всегда вычитая 100 из значения, которое должно отображаться.
Ваш код будет иметь это:
Object.defineProperty(document.getElementById("myButton"), "innerText", {
set(text) {
// Subtract 100 from the number in the text "Clicked <number> times!"
text = text.replace(/(Clicked )(\d+)( times!)/, (_, a, b, c) => a + (b - 100) + c);
// Call the prototype setter for innerText
Object.getOwnPropertyDescriptor(HTMLElement.prototype, "innerText").set.call(this, text);
}
});
Сведено в букмарклет:
javascript: {Object.defineProperty(document.getElementById("myButton"),"innerText",{set(text) {text=text.replace(/(Clicked )(\d+)( times!)/,(_,a,b,c)=>a+(b-100)+c);Object.getOwnPropertyDescriptor(HTMLElement.prototype,"innerText").set.call(this, text);}});}
Это похоже на предыдущее решение, но изменение текста производится после того, как он уже записан на кнопку innerText
. Добавьте обработчик кликов (который будет выполняться после уже подключенного), который выполняет это исправление:
document.getElementById("myButton").addEventListener("click", function () {
this.innerText = this.innerText.replace(/(Clicked )(\d+)( times!)/, (_, a, b, c) => a + (b - 100) + c);
});
Сведено в букмарклет:
javascript: document.getElementById("myButton").addEventListener("click",function(){this.innerText=this.innerText.replace(/(Clicked )(\d+)( times!)/,(_,a,b,c)=>a+(b-100)+c);})
buttonClicked2
Там должен быть код с модификацией счетчика. Затем измените каждый вызов с buttonClicked
на buttenClicked2
. Здесь я предполагаю, что это похоже на пример, где такая ссылка присутствует только в атрибуте onclick
кнопки. Чем больше мест выполняется этот вызов, тем больше функций вам может потребоваться скопировать как новые функции, перемонтировав также ссылки на эту функцию и т. д. Но здесь затрагивается только атрибут onclick
:
var buttonClicked2 = (() => {
let internal_variable = -100; // Our update
const _internal_buttonClicked = () => {
internal_variable++;
document.getElementById("myButton").innerText = `Clicked ${internal_variable} times!`;
};
return _internal_buttonClicked.bind(this);
})();
// Redirect the click
document.getElementById("myButton").setAttribute("onclick", "buttonClicked2()");
Сведено в букмарклет:
javascript: {var buttonClicked2=(()=>{let internal_variable=-100;const _internal_buttonClicked=()=>{internal_variable++;document.getElementById("myButton").innerText=`Clicked ${internal_variable} times!`;};return _internal_buttonClicked.bind(this);})();document.getElementById("myButton").setAttribute("onclick", "buttonClicked2()");}
<iframe>
В него <iframe>
поместите текущий HTML, но с модификацией кода, который вы хотите иметь. Итак страница перезагрузится, но с вашей модификацией и в <iframe>
.
if (window === top) {
// Patch the HTML
let html = document.documentElement.innerHTML
.replace("internal_variable = 0;", "internal_variable = -100;");
let iframe = `<body style = "margin:0"><iframe style = "display:block;border:0;height:100%;width:100%"></iframe></body>`;
// Replace the current document with just an <iframe>
document.write(iframe);
// Write the patched HTML to the frame document
document.querySelector("iframe").srcdoc = html;
}
Сведено в букмарклет:
javascript: if (window===top){let html = document.documentElement.innerHTML.replace("internal_variable = 0;", "internal_variable = -100;");let iframe = `<body style = "margin:0"><iframe style = "display:block;border:0;height:100%;width:100%"></iframe></body>`;document.write(iframe);document.querySelector("iframe").srcdoc=html;}
Каждое из вышеперечисленных решений имеет свои ограничения. Подходит ли один из них в реальных случаях, во многом будет зависеть от функциональности страницы.
Огромное спасибо за указание награды. Я очень надеюсь, что некоторые части этого ответа были для тебя полезны, @Kaia.
Это невозможно.