web-dev-qa-db-ja.com

「ウィンドウ」GoogleTagManagerで「postMessage」を実行できませんでした

最近、このpostMessageを複製できませんでしたエラーが発生しました。 Chrome 68、Firefox 61.0、IE11、Edgeなどの最新ブラウザーのほとんどで発生しています。

「ウィンドウ」で「postMessage」の実行に失敗しました:function (a){if(qe.$a.hasOwnProperty(a))return qe.$a[a]}を複製できませんでした。

スタックトレースは次のとおりです。

エラー:「ウィンドウ」で「postMessage」の実行に失敗しました:function (a){if(qe.$a.hasOwnProperty(a))return qe.$a[a]}を複製できませんでした。
at _reportEvent(eval at(:1:35637)、:94:35)
eval(eval at(:1:35637)、:55:5)
evalで(eval at(:1:35637)、:433:11)

DevToolsでページのソースを検索すると、gtm.jsコードフラグメントのソースとして:

gtm.js shown as source of the function

ページにGoogleタグマネージャートラッキングコードがあります。なぜこうなった?

26
cris

構造化クローンアルゴリズムで何かを複製できない場合、これは常に発生します。このアルゴリズムは_window.postMessage_によって使用されます。最初のパラメーターについて_window.postMessage_からドキュメントを読むと:

メッセージ
他のウィンドウに送信されるデータ。データは 構造化クローンアルゴリズムを使用してシリアル化されます

そして、構造化クローンアルゴリズムから説明を開きます(上記の最後のリンクを参照)。

構造化クローンアルゴリズムは、複雑なJavaScriptオブジェクトをコピーするためにHTML5仕様で定義されているアルゴリズムです。 postMessage()を介してワーカーとの間でデータを転送するとき、またはIndexedDBでオブジェクトを保存するときに内部的に使用されます。これは、無限に巡回するサイクルを回避するために、以前にアクセスした参照のマップを維持しながら、入力オブジェクトを再帰処理してクローンを構築します。

構造化クローンで動作しないもの

  • ErrorおよびFunctionオブジェクトは、構造化クローンアルゴリズムでは複製できません。そうしようとすると、_DATA_CLONE_ERR_例外がスローされます。
  • DOMノードを複製しようとすると、同様に_DATA_CLONE_ERR_例外がスローされます。
  • オブジェクトの特定のパラメーターは保持されません。

    • lastIndexオブジェクトのRegExpフィールドは保持されません。
    • プロパティ記述子、セッター、およびゲッター(および同様のメタデータのような機能)は複製されません。たとえば、オブジェクトがプロパティ記述子を使用して読み取り専用としてマークされている場合、それはデフォルトの条件であるため、複製では読み取り/書き込みになります。
    • プロトタイプチェーンはウォークされず、複製されません。

サポートされているタイプ

私はいくつかのオブジェクトでそれをテストし、これが起こっているときに次の例を示すことができます...

カスタム関数の例

_var obj = {something: function(){}};
window.postMessage(obj, '*'); // DataCloneError_

ネイティブ関数の例

_var obj = {something: window.alert};
window.postMessage(obj, '*'); // DataCloneError_

BooleanDateStringRegExpNumberArrayなどのネイティブ関数でも同じことがわかります。 。

ネイティブオブジェクトの例

_var obj = {something: document};
window.postMessage(obj, '*'); // DataCloneError_

HTML要素オブジェクトの例

_var obj = {something: document.createElement('b')};
window.postMessage(obj, '*'); // DataCloneError_

上記のThe構造化クローンアルゴリズムから説明を読むと、さらに例を書くことができますが、ここではそれで十分だと思います。

このエラーを回避するためにできること

コードでは、オブジェクトでサポートされている型(上記のリストを参照)のみを使用できます。しかし、私たちのコードではなく、このコードから開発者に連絡し、コードの修正方法を記述する必要があります。 Googleタグマネージャーの場合は、コードを修正する方法を説明して 公式Googleタグマネージャーフォーラム に書き込むことができます。

一部のブラウザの回避策

一部のブラウザでは、セキュリティ上の理由からネイティブメソッドをオーバーライドできません。たとえば、IEは_window.postMessage_のオーバーライドを許可しません。ただし、Chromeなどの他のブラウザでは、次のようにこのメソッドをオーバーライドできます。

_var postMessageTemp = window.postMessage;
window.postMessage = function(message, targetOrigin, transfer)
{
    postMessageTemp(JSON.parse(JSON.stringify(message)), targetOrigin, transfer)
};
_

ただし、windowはJavaScriptコンテキストのグローバルオブジェクトであり、prototypeからは作成されないことに注意してください。つまり、_window.prototype.postMessage = ..._でオーバーライドすることはできません。

回避策の例

_var obj = {something: window};

var postMessageTemp = window.postMessage;
window.postMessage = function(message, targetOrigin, transfer)
{
    function cloneObject(obj)
    {
        var clone = {};
        for(var i in obj)
        {
            if(typeof(obj[i]) == 'object' && obj[i] != null)
            {
                if((''+obj[i]) == '[object Window]')
                {
                    delete obj[i];
                    continue;
                }

                clone[i] = cloneObject(obj[i]);
            }
            else
                clone[i] = obj[i];
        }
        return clone;
    }

    // to avoid weird error causing by window object by JSON.stringify() execution.
    var clone = cloneObject(message);

    postMessageTemp(JSON.parse(JSON.stringify(clone)), targetOrigin, transfer)
};

window.postMessage(obj, '*');

console.log('We do not have any errors.');_

この回避策の実装方法

このオーバーライドされた_window.postMessage_関数を、Googleタグマネージャースクリプトの前にHTMLページのスクリプト部分に配置してください。しかし、より良い方法では、Google Tag Managerの開発者がこのエラーを理解して修正するのを助け、修正されたGoogle Tag Managerスクリプトを待つことができます。

13
Bharata

これらのエラーは、FacebookクローラーがJavaScriptコードを実行することにより発生します。

これらのIP(すべてFacebookのIP範囲内)とユーザーエージェントからこのエラーが発生しました。

66.220.149.14 - Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0
  31.13.115.2 - Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
 173.252.87.1 - Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
69.171.251.11 - facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)

FacebookクローラーIPの最新リストを取得するには、 https://developers.facebook.com/docs/sharing/webmasters/crawler/ からこのコマンドを参照してください。

whois -h whois.radb.net -- '-i Origin AS32934' | grep ^route

これらのIP範囲からエラーを除外するには、エラー報告メカニズムを更新する必要があります。

エラー時にユーザーのIPアドレスを判別することにより、JavaScriptでクライアント側でこれを行うことができます( JavaScriptを使用してクライアントのIPアドレスを取得する方法? を参照)。

または、サーバー側でこれを行うことができます。 ASP.NET MVCの例を次に示します。

using System.Linq;
// Requires the IPAddressRange NuGet library:
// https://www.nuget.org/packages/IPAddressRange/
using NetTools;

public class FacebookClientDetector
{
    /// <summary>
    /// The list of CIDR ranges of facebook IPs that its crawlers use.
    /// To generate, run
    ///     whois -h whois.radb.net -- '-i Origin AS32934' | grep ^route
    /// https://developers.facebook.com/docs/sharing/webmasters/crawler/
    /// </summary>
    static readonly string[] facebookIpRanges = new string[] {
        "204.15.20.0/22",
        "69.63.176.0/20",
        "66.220.144.0/20",
        "66.220.144.0/21",
        "69.63.184.0/21",
        "69.63.176.0/21",
        "74.119.76.0/22",
        "69.171.255.0/24",
        "173.252.64.0/18",
        "69.171.224.0/19",
        "69.171.224.0/20",
        "103.4.96.0/22",
        "69.63.176.0/24",
        "173.252.64.0/19",
        "173.252.70.0/24",
        "31.13.64.0/18",
        "31.13.24.0/21",
        "66.220.152.0/21",
        "66.220.159.0/24",
        "69.171.239.0/24",
        "69.171.240.0/20",
        "31.13.64.0/19",
        "31.13.64.0/24",
        "31.13.65.0/24",
        "31.13.67.0/24",
        "31.13.68.0/24",
        "31.13.69.0/24",
        "31.13.70.0/24",
        "31.13.71.0/24",
        "31.13.72.0/24",
        "31.13.73.0/24",
        "31.13.74.0/24",
        "31.13.75.0/24",
        "31.13.76.0/24",
        "31.13.77.0/24",
        "31.13.96.0/19",
        "31.13.66.0/24",
        "173.252.96.0/19",
        "69.63.178.0/24",
        "31.13.78.0/24",
        "31.13.79.0/24",
        "31.13.80.0/24",
        "31.13.82.0/24",
        "31.13.83.0/24",
        "31.13.84.0/24",
        "31.13.85.0/24",
        "31.13.86.0/24",
        "31.13.87.0/24",
        "31.13.88.0/24",
        "31.13.89.0/24",
        "31.13.90.0/24",
        "31.13.91.0/24",
        "31.13.92.0/24",
        "31.13.93.0/24",
        "31.13.94.0/24",
        "31.13.95.0/24",
        "69.171.253.0/24",
        "69.63.186.0/24",
        "31.13.81.0/24",
        "179.60.192.0/22",
        "179.60.192.0/24",
        "179.60.193.0/24",
        "179.60.194.0/24",
        "179.60.195.0/24",
        "185.60.216.0/22",
        "45.64.40.0/22",
        "185.60.216.0/24",
        "185.60.217.0/24",
        "185.60.218.0/24",
        "185.60.219.0/24",
        "129.134.0.0/16",
        "157.240.0.0/16",
        "157.240.8.0/24",
        "157.240.0.0/24",
        "157.240.1.0/24",
        "157.240.2.0/24",
        "157.240.3.0/24",
        "157.240.4.0/24",
        "157.240.5.0/24",
        "157.240.6.0/24",
        "157.240.7.0/24",
        "157.240.9.0/24",
        "157.240.10.0/24",
        "157.240.16.0/24",
        "157.240.19.0/24",
        "157.240.11.0/24",
        "157.240.12.0/24",
        "157.240.13.0/24",
        "157.240.14.0/24",
        "157.240.15.0/24",
        "157.240.17.0/24",
        "157.240.18.0/24",
        "157.240.20.0/24",
        "157.240.21.0/24",
        "157.240.22.0/24",
        "157.240.23.0/24",
        "129.134.0.0/17",
        "157.240.0.0/17",
        "69.171.250.0/24",
        "204.15.20.0/22",
        "69.63.176.0/20",
        "69.63.176.0/21",
        "69.63.184.0/21",
        "66.220.144.0/20",
        "69.63.176.0/20",
    };

    public static bool IsFacebookClient(string ip)
    {
        IPAddressRange parsedIp;
        if (!IPAddressRange.TryParse(ip, out parsedIp)) {
            return false;
        }

        return facebookIpRanges.Any(cidr => IPAddressRange.Parse(cidr).Contains(parsedIp));
    }
}
7
Yoshi