在 TWebBrowser 中将字符串传递给 Javascript 可能存在编码问题
Possible encoding issue passing string to Javascript in TWebBrowser
简介
这是我加载到 TWebBrowser 以访问 Google 地图的 HTML 页面 API:
<html>
<head>
</head>
<body>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=%GMAPSAPIKEY%&callback=initMap"></script>
<div id="editMap" style="width:600px; height:500px; margin:0; margin-bottom:0; background-color:#F9F9F9; border-right:1px solid #999; float:left;"></div>
<input type='hidden' id='DelphiVan' value='' />
<input type='hidden' id='DelphiNaar' value='' />
<input type='hidden' id='DelphiDistance' value='' />
<input type='hidden' id='DelphiPolyLine' value='' />
<script type="text/javascript">
function initMap() {
ttMapHelper.InitMap('editMap')
}
var ttMapHelper = (function () {
var map;
var directionsService, directionsDisplay;
var polyLine;
var markerA;
var markerB;
var routeChangedCallback;
var routeOrigin;
var routeDestination;
var encodedLine;
var totalDistance;
var totalDistanceText;
function getDirections(origin, destination, display) {
directionsService.route({
origin: origin,
destination: destination,
avoidTolls: true,
travelMode: google.maps.TravelMode.DRIVING, //getTravelMode($('#travelMode').val()),
unitSystem: google.maps.UnitSystem.METRIC //google.maps.UnitSystem.IMPERIAL
}, function (response, status) {
if (status === google.maps.DirectionsStatus.OK) {
if (display)
directionsDisplay.setDirections(response);
else
processDirections(response);
}
else {
alert('Could not display directions due to: ' + status);
}
});
}
function getTravelMode(mode) {
switch (mode) {
case "DRIVING":
return google.maps.TravelMode.DRIVING;
case "BICYCLING":
return google.maps.TravelMode.BICYCLING;
case "WALKING":
return google.maps.TravelMode.WALKING;
}
}
function processDirections(result) {
var route = result.routes[0];
var leg = route.legs[0];
routeOrigin = leg.start_address
routeDestination = leg.end_address
totalDistance = leg.distance.value;
totalDistanceText = leg.distance.text;
encodedLine = route.overview_polyline;
if (routeChangedCallback)
routeChangedCallback();
}
function displayPolyLine(encodedLine) {
if (polyLine) {
polyLine.setMap(null);
markerA.setMap(null);
markerB.setMap(null);
polyLine = undefined;
markerA = undefined;
markerB = undefined;
}
var coordinates = google.maps.geometry.encoding.decodePath(encodedLine);
polyLine = new google.maps.Polyline({
path: coordinates,
strokeColor: '#0000FF',
strokeOpacity: 1.0,
strokeWeight: 2
});
polyLine.setMap(map);
markerA = new google.maps.Marker({
position: coordinates[0],
label: 'A',
map: map
});
markerB = new google.maps.Marker({
position: coordinates[coordinates.length - 1],
label: 'B',
map: map
});
var bounds = new google.maps.LatLngBounds();
for (var i = 0; i < coordinates.length; i++) {
bounds.extend(coordinates[i]);
}
map.fitBounds(bounds);
}
var initMap = function (mapId) {
map = new google.maps.Map(document.getElementById(mapId), {
zoom: 7,
center: { lat: 52.2169918, lng: 5.6460789 },
mapTypeId: google.maps.MapTypeId.ROADMAP
});
directionsService = new google.maps.DirectionsService;
directionsDisplay = new google.maps.DirectionsRenderer({
draggable: false,
suppressBicyclingLayer: true,
map: map
});
directionsDisplay.addListener('directions_changed', function () {
processDirections(directionsDisplay.getDirections());
});
}
var calculateRoute = function (origin, destination, callback) {
routeChangedCallback = callback;
routeOrigin = origin;
routeDestination = destination;
getDirections(origin, destination);
}
var editRoute = function (origin, destination, callback) {
routeChangedCallback = callback;
routeOrigin = origin;
routeDestination = destination;
directionsDisplay.setOptions({ draggable: true });
getDirections(origin, destination, true);
}
var showRoute = function (encodedLine) {
displayPolyLine(encodedLine);
}
var getOrigin = function () { return routeOrigin; }
var getDestination = function () { return routeDestination; }
var getDistance = function () { return totalDistance; }
var getDistanceText = function () { return totalDistanceText; }
var getEncodedLine = function () { return encodedLine; }
return {
InitMap: initMap,
CalculateRoute: calculateRoute,
EditRoute: editRoute,
ShowRoute: showRoute,
GetOrigin: getOrigin,
GetDestination: getDestination,
GetDistance: getDistance,
GetDistanceText: getDistanceText,
GetEncodedLine: getEncodedLine
}
})();
function PrepareDelphiVars() {
document.getElementById('DelphiVan').value = ttMapHelper.GetOrigin();
document.getElementById('DelphiNaar').value = ttMapHelper.GetDestination();
document.getElementById('DelphiDistance').value = ttMapHelper.GetDistance();
document.getElementById('DelphiPolyLine').value = ttMapHelper.GetEncodedLine();
};
</script>
</body>
</html>
最初,这可以很好地确定从 A 到 B 的路由。在 WebBrowserDocumentComplete
处理程序中,我执行:
procedure TFrmGoogleMaps.ToonRoute;
begin
FHTMLWindow.execScript('ttMapHelper.EditRoute("' + FVan + '","' + FNaar + '",function (){PrepareDelphiVars();})', 'JavaScript')
end;
PrepareDelphiVars
将生成的数据放在隐藏的输入字段中,我用
检索它们
lElement := WebBrowser.OleObject.Document.getElementById('DelphiVan');
if not VarIsNull(lElement) then
FVan := lElement.getAttribute('value');
我检索到的内容之一是生成的 折线。它的形式是
i|r~Hi{y\kAqDh@wIBcEGcELcCNoHOoFDwDLqE`@mBl@sBx@g@NE]kCg@{BsFoG[eAC[b@w@`@o@pBwApCsAdWyM~^{QvDgBlKoFnOeIzEgJhEgIxAcCtB_Cf@e@Tk@Hw@e@uIm@wHIiDJ}....
如果我 copy/paste 这个字符串从 TMemo 到 Google 的 Interactive Polyline Encode/Decoder 我得到从鹿特丹到阿姆斯特丹的正确路线:
问题
我想在下次再次启动 TWebBrowser 时显示这条生成的折线(注意:它在每次创建 run-time 的表单上)。
所以我现在从 WebBrowserDocumentComplete
:
打电话
procedure TFrmGoogleMaps.ToonPolyLine;
begin
FHTMLWindow.execScript('ttMapHelper.ShowRoute("' + FPolyLine + '")', 'JavaScript')
end;
与 TMemo 中的字符串完全相同。 我现在在德国(我在 https://maps.googleapis.com/maps-api-v3/api/js/24/10/intl/nl_ALL/onion.js
秒后移动鼠标时出现 Javascript 错误):
这看起来像是一个编码问题,但我还找不到解决方案。这是我试过的:
- 在 HTML 中指定编码:
<meta charset="utf-8">
或 <meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
或 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
这表明当我在 WebBrowserDocumentComplete
中查询 (WebBrowser.Document as IHTMLDocument2).charset
时存在编码,但没有解决问题。
- 强制 TWebBrowser 使用 IE 边缘模式
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
- 用
TidURI.ParamsEncode
或其兄弟姐妹包装 FPolyLine
- 有时我最终会到达加纳阿克拉以南 300 公里的几内亚湾 ;-)。
- 组合,例如在 HTML 中指定 utf-8 并使用
UTF8Encode(FPolyLine)
注意:我的标题说 可能。这是因为 TMemo 内容和 JavaScript alert() 之间的比较似乎显示 字符串中没有差异 除了 \b
的小显示问题子字符串:
此外,Javascript 代码是从云解决方案中复制的,它运行良好。
这里可能出了什么问题,解决方案是什么?
(Delphi 10 升级 1)
FWIW,这就是我将文件的 HTML 加载到 TWebBrowser 中的方式:
procedure TFrmGoogleMaps.FormCreate(Sender: TObject);
var
lMemStream : TMemoryStream;
lFileStream: TFileStream;
lData : ANSIString;
lFileName : String;
p,l : Integer;
begin
lFileName := ExtractFilePath(ParamStr(0)) + cMapsHTMLFile;
lFileStream := TFileStream.Create(lFileName,fmOpenRead);
SetLength(lData, lFileStream.Size);
lFileStream.ReadBuffer(Pointer(lData)^, Length(lData));
lFileStream.Free;
// Replace the API key marker with the actual key:
p := System.AnsiStrings.PosEx(cAPIKeyMarker,lData);
l := Length(cAPIKeyMarker);
Delete(lData,p,l);
Insert(cGoogleMapsAPIKey,lData,p);
WebBrowser.Navigate('about:blank'); // Nodig voor initialisatie
if Assigned(WebBrowser.Document) then
begin
lMemStream := TMemoryStream.Create;
try
lMemStream.WriteBuffer(Pointer(lData)^, Length(lData));
lMemStream.Seek(0, soFromBeginning);
(WebBrowser.Document as IPersistStreamInit).Load(TStreamAdapter.Create(lMemStream));
finally
lMemStream.Free;
end;
FHTMLWindow := (WebBrowser.Document as IHTMLDocument2).parentWindow;
end;
end;
问题出在这一行:
FHTMLWindow.execScript('ttMapHelper.ShowRoute("' + FPolyLine + '")', 'JavaScript')
并且与 SQL injection 非常相似:如果 FPolyLine
是 JavaScript 格式,则您会默默地假设该值,但事实并非如此。一个简单的解决方法可能是使用
StringReplace(StringReplace(FPolyLine'\','\',[rfReplaceAll]),'"','\"',[rfReplaceAll])
但更好的解决方法是使用额外的 <input type="hidden"
,通过对象设置它的值,并通过 JavaScript.
中的名称调用它
简介
这是我加载到 TWebBrowser 以访问 Google 地图的 HTML 页面 API:
<html>
<head>
</head>
<body>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=%GMAPSAPIKEY%&callback=initMap"></script>
<div id="editMap" style="width:600px; height:500px; margin:0; margin-bottom:0; background-color:#F9F9F9; border-right:1px solid #999; float:left;"></div>
<input type='hidden' id='DelphiVan' value='' />
<input type='hidden' id='DelphiNaar' value='' />
<input type='hidden' id='DelphiDistance' value='' />
<input type='hidden' id='DelphiPolyLine' value='' />
<script type="text/javascript">
function initMap() {
ttMapHelper.InitMap('editMap')
}
var ttMapHelper = (function () {
var map;
var directionsService, directionsDisplay;
var polyLine;
var markerA;
var markerB;
var routeChangedCallback;
var routeOrigin;
var routeDestination;
var encodedLine;
var totalDistance;
var totalDistanceText;
function getDirections(origin, destination, display) {
directionsService.route({
origin: origin,
destination: destination,
avoidTolls: true,
travelMode: google.maps.TravelMode.DRIVING, //getTravelMode($('#travelMode').val()),
unitSystem: google.maps.UnitSystem.METRIC //google.maps.UnitSystem.IMPERIAL
}, function (response, status) {
if (status === google.maps.DirectionsStatus.OK) {
if (display)
directionsDisplay.setDirections(response);
else
processDirections(response);
}
else {
alert('Could not display directions due to: ' + status);
}
});
}
function getTravelMode(mode) {
switch (mode) {
case "DRIVING":
return google.maps.TravelMode.DRIVING;
case "BICYCLING":
return google.maps.TravelMode.BICYCLING;
case "WALKING":
return google.maps.TravelMode.WALKING;
}
}
function processDirections(result) {
var route = result.routes[0];
var leg = route.legs[0];
routeOrigin = leg.start_address
routeDestination = leg.end_address
totalDistance = leg.distance.value;
totalDistanceText = leg.distance.text;
encodedLine = route.overview_polyline;
if (routeChangedCallback)
routeChangedCallback();
}
function displayPolyLine(encodedLine) {
if (polyLine) {
polyLine.setMap(null);
markerA.setMap(null);
markerB.setMap(null);
polyLine = undefined;
markerA = undefined;
markerB = undefined;
}
var coordinates = google.maps.geometry.encoding.decodePath(encodedLine);
polyLine = new google.maps.Polyline({
path: coordinates,
strokeColor: '#0000FF',
strokeOpacity: 1.0,
strokeWeight: 2
});
polyLine.setMap(map);
markerA = new google.maps.Marker({
position: coordinates[0],
label: 'A',
map: map
});
markerB = new google.maps.Marker({
position: coordinates[coordinates.length - 1],
label: 'B',
map: map
});
var bounds = new google.maps.LatLngBounds();
for (var i = 0; i < coordinates.length; i++) {
bounds.extend(coordinates[i]);
}
map.fitBounds(bounds);
}
var initMap = function (mapId) {
map = new google.maps.Map(document.getElementById(mapId), {
zoom: 7,
center: { lat: 52.2169918, lng: 5.6460789 },
mapTypeId: google.maps.MapTypeId.ROADMAP
});
directionsService = new google.maps.DirectionsService;
directionsDisplay = new google.maps.DirectionsRenderer({
draggable: false,
suppressBicyclingLayer: true,
map: map
});
directionsDisplay.addListener('directions_changed', function () {
processDirections(directionsDisplay.getDirections());
});
}
var calculateRoute = function (origin, destination, callback) {
routeChangedCallback = callback;
routeOrigin = origin;
routeDestination = destination;
getDirections(origin, destination);
}
var editRoute = function (origin, destination, callback) {
routeChangedCallback = callback;
routeOrigin = origin;
routeDestination = destination;
directionsDisplay.setOptions({ draggable: true });
getDirections(origin, destination, true);
}
var showRoute = function (encodedLine) {
displayPolyLine(encodedLine);
}
var getOrigin = function () { return routeOrigin; }
var getDestination = function () { return routeDestination; }
var getDistance = function () { return totalDistance; }
var getDistanceText = function () { return totalDistanceText; }
var getEncodedLine = function () { return encodedLine; }
return {
InitMap: initMap,
CalculateRoute: calculateRoute,
EditRoute: editRoute,
ShowRoute: showRoute,
GetOrigin: getOrigin,
GetDestination: getDestination,
GetDistance: getDistance,
GetDistanceText: getDistanceText,
GetEncodedLine: getEncodedLine
}
})();
function PrepareDelphiVars() {
document.getElementById('DelphiVan').value = ttMapHelper.GetOrigin();
document.getElementById('DelphiNaar').value = ttMapHelper.GetDestination();
document.getElementById('DelphiDistance').value = ttMapHelper.GetDistance();
document.getElementById('DelphiPolyLine').value = ttMapHelper.GetEncodedLine();
};
</script>
</body>
</html>
最初,这可以很好地确定从 A 到 B 的路由。在 WebBrowserDocumentComplete
处理程序中,我执行:
procedure TFrmGoogleMaps.ToonRoute;
begin
FHTMLWindow.execScript('ttMapHelper.EditRoute("' + FVan + '","' + FNaar + '",function (){PrepareDelphiVars();})', 'JavaScript')
end;
PrepareDelphiVars
将生成的数据放在隐藏的输入字段中,我用
lElement := WebBrowser.OleObject.Document.getElementById('DelphiVan');
if not VarIsNull(lElement) then
FVan := lElement.getAttribute('value');
我检索到的内容之一是生成的 折线。它的形式是
i|r~Hi{y\kAqDh@wIBcEGcELcCNoHOoFDwDLqE`@mBl@sBx@g@NE]kCg@{BsFoG[eAC[b@w@`@o@pBwApCsAdWyM~^{QvDgBlKoFnOeIzEgJhEgIxAcCtB_Cf@e@Tk@Hw@e@uIm@wHIiDJ}....
如果我 copy/paste 这个字符串从 TMemo 到 Google 的 Interactive Polyline Encode/Decoder 我得到从鹿特丹到阿姆斯特丹的正确路线:
问题
我想在下次再次启动 TWebBrowser 时显示这条生成的折线(注意:它在每次创建 run-time 的表单上)。
所以我现在从 WebBrowserDocumentComplete
:
procedure TFrmGoogleMaps.ToonPolyLine;
begin
FHTMLWindow.execScript('ttMapHelper.ShowRoute("' + FPolyLine + '")', 'JavaScript')
end;
与 TMemo 中的字符串完全相同。 我现在在德国(我在 https://maps.googleapis.com/maps-api-v3/api/js/24/10/intl/nl_ALL/onion.js
秒后移动鼠标时出现 Javascript 错误):
这看起来像是一个编码问题,但我还找不到解决方案。这是我试过的:
- 在 HTML 中指定编码:
<meta charset="utf-8">
或<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
或<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
这表明当我在WebBrowserDocumentComplete
中查询(WebBrowser.Document as IHTMLDocument2).charset
时存在编码,但没有解决问题。 - 强制 TWebBrowser 使用 IE 边缘模式
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
- 用
TidURI.ParamsEncode
或其兄弟姐妹包装FPolyLine
- 有时我最终会到达加纳阿克拉以南 300 公里的几内亚湾 ;-)。 - 组合,例如在 HTML 中指定 utf-8 并使用
UTF8Encode(FPolyLine)
注意:我的标题说 可能。这是因为 TMemo 内容和 JavaScript alert() 之间的比较似乎显示 字符串中没有差异 除了 \b
的小显示问题子字符串:
此外,Javascript 代码是从云解决方案中复制的,它运行良好。
这里可能出了什么问题,解决方案是什么?
(Delphi 10 升级 1)
FWIW,这就是我将文件的 HTML 加载到 TWebBrowser 中的方式:
procedure TFrmGoogleMaps.FormCreate(Sender: TObject);
var
lMemStream : TMemoryStream;
lFileStream: TFileStream;
lData : ANSIString;
lFileName : String;
p,l : Integer;
begin
lFileName := ExtractFilePath(ParamStr(0)) + cMapsHTMLFile;
lFileStream := TFileStream.Create(lFileName,fmOpenRead);
SetLength(lData, lFileStream.Size);
lFileStream.ReadBuffer(Pointer(lData)^, Length(lData));
lFileStream.Free;
// Replace the API key marker with the actual key:
p := System.AnsiStrings.PosEx(cAPIKeyMarker,lData);
l := Length(cAPIKeyMarker);
Delete(lData,p,l);
Insert(cGoogleMapsAPIKey,lData,p);
WebBrowser.Navigate('about:blank'); // Nodig voor initialisatie
if Assigned(WebBrowser.Document) then
begin
lMemStream := TMemoryStream.Create;
try
lMemStream.WriteBuffer(Pointer(lData)^, Length(lData));
lMemStream.Seek(0, soFromBeginning);
(WebBrowser.Document as IPersistStreamInit).Load(TStreamAdapter.Create(lMemStream));
finally
lMemStream.Free;
end;
FHTMLWindow := (WebBrowser.Document as IHTMLDocument2).parentWindow;
end;
end;
问题出在这一行:
FHTMLWindow.execScript('ttMapHelper.ShowRoute("' + FPolyLine + '")', 'JavaScript')
并且与 SQL injection 非常相似:如果 FPolyLine
是 JavaScript 格式,则您会默默地假设该值,但事实并非如此。一个简单的解决方法可能是使用
StringReplace(StringReplace(FPolyLine'\','\',[rfReplaceAll]),'"','\"',[rfReplaceAll])
但更好的解决方法是使用额外的 <input type="hidden"
,通过对象设置它的值,并通过 JavaScript.