【问题标题】:Possible encoding issue passing string to Javascript in TWebBrowser在 TWebBrowser 中将字符串传递给 Javascript 的可能编码问题
【发布时间】:2016-04-28 12:08:14
【问题描述】:

简介

这是我加载到 TWebBrowser 以访问 Google 地图 API 的 HTML 页面:

<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}....

如果我将此字符串从 TMemo 复制/粘贴到 Google 的 Interactive Polyline Encode/Decoder,我会得到从鹿特丹到阿姆斯特丹的正确路线:

问题

我想在下次再次启动 TWebBrowser 时显示这条生成的折线(注意:它位于每次运行时创建的表单上)。
所以我现在从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 中指定编码: &lt;meta charset="utf-8"&gt;&lt;meta http-equiv="Content-Type" content="text/html; charset=windows-1251"&gt;&lt;meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"&gt;
    这表明当我在WebBrowserDocumentComplete 中查询(WebBrowser.Document as IHTMLDocument2).charset 时存在编码,但不能解决问题。
  • 强制 TWebBrowser 使用带有&lt;meta http-equiv="X-UA-Compatible" content="IE=edge" /&gt; 的 IE 边缘模式
  • FPolyLineTidURI.ParamsEncode 或其兄弟姐妹包装在一起 - 有时我最终会在加纳阿克拉以南 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;

【问题讨论】:

  • 您将字符串发送到 JavaScript,所以我希望使用 JavaScript 字符串编码函数,而不是 URL 参数编码函数。

标签: javascript delphi encoding twebbrowser delphi-10-seattle


【解决方案1】:

问题出在这一行:

FHTMLWindow.execScript('ttMapHelper.ShowRoute("' + FPolyLine + '")', 'JavaScript')

并且与SQL injection 非常相似:如果FPolyLine 是JavaScript 格式,那么您会默默地假设该值,但事实并非如此。一个简单的解决方法可能是使用

StringReplace(StringReplace(FPolyLine'\','\\',[rfReplaceAll]),'"','\"',[rfReplaceAll])

但更好的解决方法是使用额外的&lt;input type="hidden",通过对象设置它的值,然后从 JavaScript 中通过名称调用它。

【讨论】:

  • 谢谢 Stijn! “我们现在感觉很愚蠢”,我的同事网络开发人员说 ;-) 与此同时,我们实际上已经考虑过您的替代方案,并且现在将实施。
猜你喜欢
  • 2016-06-01
  • 2018-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多