最近、この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
コードフラグメントのソースとして:
ページにGoogleタグマネージャートラッキングコードがあります。なぜこうなった?
構造化クローンアルゴリズムで何かを複製できない場合、これは常に発生します。このアルゴリズムは_window.postMessage
_によって使用されます。最初のパラメーターについて_window.postMessage
_からドキュメントを読むと:
メッセージ
他のウィンドウに送信されるデータ。データは 構造化クローンアルゴリズム 。を使用してシリアル化されます
そして、構造化クローンアルゴリズムから説明を開きます(上記の最後のリンクを参照)。
構造化クローンアルゴリズムは、複雑なJavaScriptオブジェクトをコピーするためにHTML5仕様で定義されているアルゴリズムです。
postMessage()
を介してワーカーとの間でデータを転送するとき、またはIndexedDB
でオブジェクトを保存するときに内部的に使用されます。これは、無限に巡回するサイクルを回避するために、以前にアクセスした参照のマップを維持しながら、入力オブジェクトを再帰処理してクローンを構築します。構造化クローンで動作しないもの
Error
およびFunction
オブジェクトは、構造化クローンアルゴリズムでは複製できません。そうしようとすると、_DATA_CLONE_ERR
_例外がスローされます。DOM
ノードを複製しようとすると、同様に_DATA_CLONE_ERR
_例外がスローされます。オブジェクトの特定のパラメーターは保持されません。
lastIndex
オブジェクトのRegExp
フィールドは保持されません。- プロパティ記述子、セッター、およびゲッター(および同様のメタデータのような機能)は複製されません。たとえば、オブジェクトがプロパティ記述子を使用して読み取り専用としてマークされている場合、それはデフォルトの条件であるため、複製では読み取り/書き込みになります。
- プロトタイプチェーンはウォークされず、複製されません。
サポートされているタイプ
- すべてのプリミティブ型(注:ただし、シンボルではありません)
Boolean
オブジェクトString
オブジェクトDate
RegExp
(注:lastIndexフィールドは保持されません。)Blob
File
FileList
ArrayBuffer
ArrayBufferView
(注:これは基本的に、Int32Arrayなどのすべての型付き配列を意味します)ImageData
Array
Object
(注:これにはプレーンオブジェクトのみが含まれます(オブジェクトリテラルなど))Map
Set
私はいくつかのオブジェクトでそれをテストし、これが起こっているときに次の例を示すことができます...
カスタム関数の例
_var obj = {something: function(){}};
window.postMessage(obj, '*'); // DataCloneError
_
ネイティブ関数の例
_var obj = {something: window.alert};
window.postMessage(obj, '*'); // DataCloneError
_
Boolean
、Date
、String
、RegExp
、Number
、Array
などのネイティブ関数でも同じことがわかります。 。
ネイティブオブジェクトの例
_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スクリプトを待つことができます。
これらのエラーは、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));
}
}