web-dev-qa-db-ja.com

Javascript関数(クライアント側)の実行を停止するか、微調整します

サイトからの1行の実行を停止して、1行を除いてページ全体がブラウザーによって読み取られるようにします。または、ブラウザがそのjavascript関数の実行をスキップする場合もあります。

OR

Javascriptの乱数生成関数が乱数を生成しないようにjavascriptを微調整する方法はありますが、必要な数は...

スクリプトがホストされているサイトにアクセスできないため、これはすべてクライアント側で行う必要があります。

26
Yousuf

Firefoxは現在 beforescriptexecuteイベント (現在 2011年3月22日にリリースされたバージョン4

そのイベントと // @run-at document-startディレクティブ により、FirefoxとGreasemonkeyは特定の<script>タグをうまく傍受しているように見えます。

これはChrome + Tampermonkeyではまだ不可能です。 Firefox + Greasemonkey以外の場合は、以下の他の回答に示されているように、完全なブラウザ拡張機能を作成する手法を使用する必要があります。

checkForBadJavascripts関数 これをカプセル化します。たとえば、ページに次のような<script>タグがあるとします。

<script>
    alert ("Sorry, Sucka!  You've got no money left.");
</script>

次のようにcheckForBadJavascriptsを使用できます。

checkForBadJavascripts ( [
    [   false, 
        /Sorry, Sucka/, 
        function () {
            addJS_Node ('alert ("Hooray, you\'re a millionaire.");');
        } 
    ]
] );

より良いメッセージを取得します。 (^ _ ^)
詳細については、checkForBadJavascriptsのインラインドキュメントを参照してください。


完全なスクリプトでデモを見るには、最初に jsBinのこのページ にアクセスしてください。 3行のテキストが表示され、そのうち2行はJSによって追加されました。

さて、 このスクリプトをインストールしてくださいソースを表示 ;それも下にあります。)そしてページに戻ってください。 GMスクリプトが1つの不良タグを削除し、別のタグを「良好な」JSに置き換えたことがわかります。


Firefoxのみがbeforescriptexecuteイベントをサポートしていることに注意してください。また、同等の機能が指定されていないため、HTML5仕様から削除されました。



Complete GM script example(GitHubおよびjsBinのものと同じ):

このHTMLを考えると:

<body onload="init()">
<script type="text/javascript" src="http://jsbin.com/evilExternalJS/js"></script>
<script type="text/javascript" language="javascript">
    function init () {
        var newParagraph            = document.createElement ('p');
        newParagraph.textContent    = "I was added by the old, evil init() function!";
        document.body.appendChild (newParagraph);
    }
</script>
<p>I'm some initial text.</p>
</body>


次のGreasemonkeyスクリプトを使用します。

// ==UserScript==
// @name        _Replace evil Javascript
// @include     http://jsbin.com/ogudon*
// @run-at      document-start
// ==/UserScript==

/****** New "init" function that we will use
    instead of the old, bad "init" function.
*/
function init () {
    var newParagraph            = document.createElement ('p');
    newParagraph.textContent    = "I was added by the new, good init() function!";
    document.body.appendChild (newParagraph);
}

/*--- Check for bad scripts to intercept and specify any actions to take.
*/
checkForBadJavascripts ( [
    [false, /old, evil init()/, function () {addJS_Node (init);} ],
    [true,  /evilExternalJS/i,  null ]
] );

function checkForBadJavascripts (controlArray) {
    /*--- Note that this is a self-initializing function.  The controlArray
        parameter is only active for the FIRST call.  After that, it is an
        event listener.

        The control array row is  defines like so:
        [bSearchSrcAttr, identifyingRegex, callbackFunction]
        Where:
            bSearchSrcAttr      True to search the SRC attribute of a script tag
                                false to search the TEXT content of a script tag.
            identifyingRegex    A valid regular expression that should be unique
                                to that particular script tag.
            callbackFunction    An optional function to execute when the script is
                                found.  Use null if not needed.
    */
    if ( ! controlArray.length) return null;

    checkForBadJavascripts      = function (zEvent) {

        for (var J = controlArray.length - 1;  J >= 0;  --J) {
            var bSearchSrcAttr      = controlArray[J][0];
            var identifyingRegex    = controlArray[J][1];

            if (bSearchSrcAttr) {
                if (identifyingRegex.test (zEvent.target.src) ) {
                    stopBadJavascript (J);
                    return false;
                }
            }
            else {
                if (identifyingRegex.test (zEvent.target.textContent) ) {
                    stopBadJavascript (J);
                    return false;
                }
            }
        }

        function stopBadJavascript (controlIndex) {
            zEvent.stopPropagation ();
            zEvent.preventDefault ();

            var callbackFunction    = controlArray[J][2];
            if (typeof callbackFunction == "function")
                callbackFunction ();

            //--- Remove the node just to clear clutter from Firebug inspection.
            zEvent.target.parentNode.removeChild (zEvent.target);

            //--- Script is intercepted, remove it from the list.
            controlArray.splice (J, 1);
            if ( ! controlArray.length) {
                //--- All done, remove the listener.
                window.removeEventListener (
                    'beforescriptexecute', checkForBadJavascripts, true
                );
            }
        }
    }

    /*--- Use the "beforescriptexecute" event to monitor scipts as they are loaded.
        See https://developer.mozilla.org/en/DOM/element.onbeforescriptexecute
        Note that it does not work on acripts that are dynamically created.
    */
    window.addEventListener ('beforescriptexecute', checkForBadJavascripts, true);

    return checkForBadJavascripts;
}

function addJS_Node (text, s_URL, funcToRun) {
    var D                                   = document;
    var scriptNode                          = D.createElement ('script');
    scriptNode.type                         = "text/javascript";
    if (text)       scriptNode.textContent  = text;
    if (s_URL)      scriptNode.src          = s_URL;
    if (funcToRun)  scriptNode.textContent  = '(' + funcToRun.toString() + ')()';

    var targ = D.getElementsByTagName ('head')[0] || D.body || D.documentElement;
    //--- Don't error check here. if DOM not available, should throw error.
    targ.appendChild (scriptNode);
}
36
Brock Adams

答えは、提供されなかった詳細によって異なります(正確なページとコード行が最適です)が、一般的な方法は次のとおりです。

  1. 問題のあるJSコードがすぐに起動しないDOMContentLoadedの後に発生する)場合は、Greasemonkeyを使用して問題のあるコードを置き換えることができます。例えば:

    var scriptNode          = document.createElement ("script");
    scriptNode.textContent  = "Your JS code here";
    document.head.appendChild (scriptNode);
    

    完了。

  2. JSコードがすぐに起動する場合、より複雑になります。
    まず、スクリプトのコピーを取得して、必要な変更を加えます。これをローカルに保存します。

  3. 問題のあるスクリプトはファイルにありますか、それともメインページのHTMLにありますか(<script src="Some File><script>Mess O' Code</script>)?

  4. スクリプトがファイル内にある場合は、 Adblock Plus をインストールし、それを使用してそのスクリプトの読み込みをブロックします。次に、Greasemonkeyを使用して、変更したコードをページに追加します。例えば:

    var scriptNode          = document.createElement ("script");
    scriptNode.setAttribute ("src", "Point to your modified JS file here.");
    document.head.appendChild (scriptNode);
    
  5. スクリプトがメインのHTMLページにある場合は、 NoScript (best)または YesScript のいずれかをインストールし、それを使用してそのサイトからJavaScriptをブロックします。
    これは、Greasemonkeyを使用して、そのサイトから実行するすべてのスクリプトを置き換える必要があることを意味します。

7
Brock Adams

beforescriptexecuteはFirefoxでは機能しなくなり、Chromeでも機能しなくなりました。幸いなことに、 MutationObserver を使用する代替手段があります。これは非常に広くサポートされています。一般的な考え方は、ページロードの開始時にMutationObserverを追加することです。これにより、新しいノードがDOMに追加されるたびにコールバックが実行されます。コールバック内で、変更または削除する<script>タグの存在を確認します。存在する場合は、改ざんできます(たとえば、textContentを変更したり、srcポイントを別の場所に配置したりできます)。 afterコールバックが終了するだけで、新しく追加されたスクリプトタグが実行されるため、これはページ上のJavascriptをインターセプトして変更する効果的な方法です。ライブスニペットの例を次に示します。

<script>
  // This is the userscript code, which runs at the beginning of pageload
  // Say we wanted to prevent the loading of the jQuery script tag below:
  new MutationObserver((_, observer) => {
    const jqueryScriptTag = document.querySelector('script[src*="jquery"]');
    if (jqueryScriptTag) {
      console.log('Found jQuery script tag; now removing it!');
      jqueryScriptTag.remove();
      // We've done what we needed to do, no need for the MutationObserver anymore:
      observer.disconnect();
    }
  })
    .observe(document.documentElement, { childList: true, subtree: true });
</script>
<div>Example content</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<script>
  console.log('After jQuery script tag. If done right, $ should be undefined:');
  console.log(typeof $);
</script>

これは、jQueryがスタックオーバーフローの<head>に読み込まれないようにするユーザースクリプトの例です。

// ==UserScript==
// @name             Example jQuery removal
// @include          https://stackoverflow.com*
// @run-at           document-start
// @grant            none
// ==/UserScript==

if (document.head) {
  throw new Error('Head already exists - make sure to enable instant script injection');
}
new MutationObserver((_, observer) => {
  const jqueryScriptTag = document.querySelector('script[src*="jquery"]');
  if (jqueryScriptTag) {
    jqueryScriptTag.remove();
    observer.disconnect();
  }
})
  .observe(document.documentElement, { childList: true, subtree: true });

これをインストールすると、jQueryの読み込みが失敗し、StackOverflowのJSによって多くのエラーが発生することがわかります。

できるだけ早くMutationObserverをアタッチしてください。ページが@run-at document-start内に何かをロードする前に、<head>をアタッチする必要があります。 (Tampermonkey/Chromeを使用している場合、これを確実に実現するには、実験的なインスタントスクリプトインジェクションを有効にする必要がある場合があります-Tampermonkeyの設定、構成モード:詳細に移動し、一番下までスクロールして、実験的なインジェクトモードをインスタントに設定します。)

他の人のためにユーザースクリプトを書いている場合、このテクニックを使用している場合は、インジェクションがnotインスタントであるため、インスタントスクリプトインジェクションの手順を必ず含めてくださいChromeではデフォルトで。

オブザーバーはを使用して接続されていることに注意してください

.observe(document.documentElement, { childList: true, subtree: true });

これにより、オブザーバーが<html>要素にアタッチされ、childList: trueで追加および削除された直接の子が監視され、追加および削除されたノードが監視されます子孫内の任意の場所subtree: true。このような再帰リスナーは便利ですが、大きな動的ページでは計算コストもかかるため、目的が達成されたら必ず削除してください。

巨大なページでは、すべてのミューテーションでquerySelectorを呼び出すとコストがかかる可能性があるため、mutations(オブザーバーのコールバックの最初のパラメーター)とミューテーションのaddedNodes代わりに:

<script>
  // This is the userscript code, which runs at the beginning of pageload
  // Say we wanted to prevent the loading of the jQuery script tag below:
  new MutationObserver((mutations, observer) => {
    for (const mutation of mutations) {
      for (const addedNode of mutation.addedNodes) {
        if (addedNode.nodeType === 1 && addedNode.matches('script[src*="jquery"]')) {
          console.log('Found jQuery script tag; now removing it!');
          addedNode.remove();
          // We've done what we needed to do, no need for the MutationObserver anymore:
          observer.disconnect();
          return;
        }
      }
    }
  })
    .observe(document.documentElement, { childList: true, subtree: true });
</script>
<div>Example content</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<script>
  console.log('After jQuery script tag. If done right, $ should be undefined:');
  console.log(typeof $);
</script>

オブザーバーコールバック内でtextContentに割り当てることにより、インラインスクリプトを微調整することもできます。次のスニペットは、乱数ジェネレーター関数を変更して、1〜6ではなく常に10を返す方法を示しています。

<script>
  // This is the userscript code, which runs at the beginning of pageload
  new MutationObserver((mutations, observer) => {
    for (const mutation of mutations) {
      for (const addedNode of mutation.addedNodes) {
        if (addedNode.textContent.includes('const randomNum')) {
          addedNode.textContent = addedNode.textContent.replace(
            /const randomNum.*/,
            'const randomNum = 10;'
          );
          observer.disconnect();
          return;
        }
      }
    }
  })
    .observe(document.documentElement, { childList: true, subtree: true });
</script>
<button>Roll Die</button>
<div>Results: <span></span></div>
<script>
const [button, span] = ['button', 'span'].map(tag => document.querySelector(tag));
button.onclick = () => {
  const randomNum = Math.ceil(Math.random() * 6);
  span.textContent = randomNum;
};
</script>

上記は、othersのユーザースクリプトを作成するときに使用できる手法です。ただし、ユーザースクリプトがyourself専用の場合は、状況によってはより簡単に機能する方法があります。質問に対するこの回答を参照してください Chrome Dev Tools-javascriptを変更してリロード :Chrome Devtoolsの[ソース]-> [オーバーライド]タブに移動し、ローカルオーバーライドを有効にすると、サイトのバージョンをダウンロードする代わりに.jsのローカルコピーをロードするようにブラウザに指示できます。その後、必要に応じてローカルコピーを編集すると、組み込みスクリプトの代わりに実行されます。これは特に便利です。大きなJavascriptファイルを微調整する場合-それははるかに MutationObserverアプローチよりも管理しやすいです。

ただし、いくつかの欠点があります。

  • インライン<script>// code here</script>タグを変更するには、ページのローカルコピーをダウンロードする必要があります。 (したがって、ページがHTML応答を介して動的コンテンツを提供する場合、新しいコンテンツを使用するには、オーバーライド用に.htmlを再保存して再調整するか、に戻る必要があります。 MutationObserverメソッド。)
  • これはテクニックですdevelopers(ある程度の実験の後)理解できるかもしれませんが、それほどユーザーフレンドリーではありません。カジュアルなuserscriptの消費者に説明したいようなものではないかもしれません。
0