使用DCEF GetResourceHandler的blob urls和跨源错误

时间:2015-02-03 16:36:44

标签: delphi chromium-embedded

我在Delphi 2007中使用最新的Delphi Chromium Embedded包装器(使用libcef 3.1750.1738,我相信由DCEF3创建者编译)实现了GetResourceHandler调用。我将我的处理程序基于magpcss网站上发布的一些python代码(有一些更改)。

一切都运作良好,除了一些事情。首先,“blob:”网址不起作用,我正在摸索如何让他们去。我一般都不了解它们,所以这是问题的一部分。

其次,使用我的资源处理程序时会弹出一些跨源的东西,否则就会弹出。这是我对https://maps.google.com/的测试。我明白了:

GET blob:https%3A//map.google.com/93e08d0a-b8b3-4c60-8a82-71d424b0893c 404 (Not Found) rs=ACT90oHHUBc59MzTbg1AU48l7w-F8f1yuA:1107
GET blob:https%3A//map.google.com/4c89a387-5958-4c19-ae9c-0640007db009 404 (Not Found) rs=ACT90oHHUBc59MzTbg1AU48l7w-F8f1yuA:1107
GET blob:https%3A//map.google.com/bd839949-a77b-43a1-9f47-1cb841f858a9 404 (Not Found) /maps/_/js/k=maps.m.en.oD0-LnUwxmc.O/m=sy437,sy444,sy446,sy486,sy494,wrc,sy438,sy436,vw,sy139,sy220…:134
XMLHttpRequest cannot load https://mt0.google.com/vt?pb=!1m8!4m7!2u15!5m2!1x450831026!2x3361961734!6m2!1x451029165!2x3362284457!2m1!1e0!3m1!5e1105!4e5!18m1!1b1. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://map.google.com' is therefore not allowed access. /maps/@45.0930104,-93.28442,15z:1
XMLHttpRequest cannot load https://mt0.google.com/vt/pb=!1m8!3m7!1m2!1u2020096!2u3014144!2m2!1u1024!2u768!3i15!2m3!1e0!2sm!3i290!3m2!2sen!5e1105!4e4!11m2!1e2!2b1!20m1!1b1. No 'Access-Control-Allow-    Origin' header is present on the requested resource. Origin 'https://map.google.com' is therefore not allowed access. /maps/@45.0930104,-93.28442,15z:1

然而,当省略我的资源处理程序并在没有它的情况下使用CEF时,这些问题都不会发生。我发现我可以通过禁用Web安全来解决CORS问题。这似乎不太理想,而且在没有我的资源处理程序的情况下使用CEF时也不需要。

这是我的资源处理程序代码:

unit WebInterceptHandler;

interface

uses
  ceflib, Classes;

type
  TWebInterceptHandler = class(TCefResourceHandlerOwn)
  protected
    FDataStream: TMemoryStream;
    FResponseHeadersReadyCallback: ICefCallback;
    FOffsetRead: NativeUInt;
    FResponse: ICefResponse;

    function ProcessRequest(const Request: ICefRequest;
        const Callback: ICefCallback): Boolean; override;
    procedure GetResponseHeaders(const Response: ICefResponse;
        out ResponseLength: Int64; out RedirectUrl: ustring); override;
    function ReadResponse(const DataOut: Pointer; BytesToRead: Integer;
        var BytesRead: Integer; const Callback: ICefCallback): Boolean; override;
  public
    constructor Create(const Browser: ICefBrowser; const Frame: ICefFrame;
        const SchemeName: ustring; const Request: ICefRequest); override;
    destructor Destroy; override;
  end;

implementation

uses windows, sysutils;

type
  TWebInterceptHandlerClient = class(TCefUrlRequestClientOwn)
  private
    FResourceHandler: TWebInterceptHandler;
  protected
    procedure OnDownloadData(const Request: ICefUrlRequest; Data: Pointer;
        DataLength: NativeUInt); override;
    procedure OnRequestComplete(const Request: ICefUrlRequest); override;
  end;

{ TWebInterceptHandlerClient }

procedure TWebInterceptHandlerClient.OnDownloadData(const Request: ICefUrlRequest;
  Data: Pointer; DataLength: NativeUInt);
begin
  inherited;

  FResourceHandler.FDataStream.Write(Data^, DataLength);

  //OutputDebugStringW(PWideChar(Request.GetRequest.Url));
end;

procedure TWebInterceptHandlerClient.OnRequestComplete(const Request: ICefUrlRequest);
begin
  inherited;

  FResourceHandler.FResponse := Request.GetResponse;
  if FResourceHandler.FResponseHeadersReadyCallback <> nil then
    FResourceHandler.FResponseHeadersReadyCallback.Cont;
end;


{ TWebInterceptHandler }

constructor TWebInterceptHandler.Create(const Browser: ICefBrowser;
  const Frame: ICefFrame; const SchemeName: ustring;
  const Request: ICefRequest);
begin
  inherited;

  FDataStream := TMemoryStream.Create;
end;

//function HTTPDecode(const AStr: ustring): rbstring;
//var
//  Sp, Rp, Cp: PAnsiChar;
//  src: rbstring;
//begin
//  src := rbstring(AStr);
//  SetLength(Result, Length(src));
//  Sp := PAnsiChar(src);
//  Rp := PAnsiChar(Result);
//  while Sp^ <> #0 do
//  begin
//    case Sp^ of
//      '+': Rp^ := ' ';
//      '%': begin
//             Inc(Sp);
//             if Sp^ = '%' then
//               Rp^ := '%'
//             else
//             begin
//               Cp := Sp;
//               Inc(Sp);
//               if (Cp^ <> #0) and (Sp^ <> #0) then
//                 Rp^ := AnsiChar(StrToInt('$' + Char(Cp^) + Char(Sp^)))
//               else
//               begin
//                 Result := '';
//                 Exit;
//               end;
//             end;
//           end;
//    else
//      Rp^ := Sp^;
//    end;
//    Inc(Rp);
//    Inc(Sp);
//  end;
//  SetLength(Result, Rp - PAnsiChar(Result));
//end;

destructor TWebInterceptHandler.Destroy;
begin
  FDataStream.Free;

  inherited;
end;

function TWebInterceptHandler.ProcessRequest(const Request: ICefRequest;
      const Callback: ICefCallback): Boolean;
var
  wrc: TWebInterceptHandlerClient;
//  url: ustring;
//  i: Integer;
//  headerMap: ICefStringMultimap;
begin
  //headerMap := TCefStringMultimapOwn.Create;

  //OutputDebugStringW(PWideChar(Request.Url));

  //Request.GetHeaderMap(headerMap);
  //if headerMap.Size <> 0 then
  //  for i := 0 to headerMap.Size - 1 do
  //    OutputDebugStringW(PWideChar(headerMap.Key[i] + ': ' + headerMap.Value[i]));

  //if Pos('blob:', WideLowerCase(Request.Url)) = 1 then
  //  request.Url := HTTPDecode(Copy(Request.Url, 1, Length(Request.Url) - Length('blob:')));

  FOffsetRead := 0;

  FResponseHeadersReadyCallback := Callback;
  wrc := TWebInterceptHandlerClient.Create;
  wrc.FResourceHandler := Self;
  TCefUrlRequestRef.New(request, wrc);

  Result := True;
end;

procedure TWebInterceptHandler.GetResponseHeaders(const Response: ICefResponse;
    out ResponseLength: Int64; out RedirectUrl: ustring);
var
  headerMap: ICefStringMultimap;
begin
  headerMap := TCefStringMultimapOwn.Create;
  Response.Status := FResponse.Status;
  Response.StatusText := FResponse.StatusText;
  Response.MimeType := FResponse.MimeType;

  FResponse.GetHeaderMap(headerMap);
  if headerMap.Size <> 0 then
    FResponse.SetHeaderMap(headerMap);

  ResponseLength := FDataStream.Size;
end;

function TWebInterceptHandler.ReadResponse(const DataOut: Pointer; BytesToRead:
    Integer; var BytesRead: Integer; const Callback: ICefCallback): Boolean;
begin
  if FOffsetRead < FDataStream.Size then
  begin
    BytesRead := BytesToRead;

    Move(Pointer(NativeUInt(FDataStream.Memory) + FOffsetRead)^, DataOut^,
        BytesRead);

    Inc(FOffsetRead, BytesRead);

    Result := True;
  end
  else
    Result := False;
end;

end.

我从测试应用中调用的是:

object MainForm: TMainForm
  Left = 0
  Top = 0
  Caption = 'MainForm'
  ClientHeight = 716
  ClientWidth = 752
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object URLBox: TEdit
    Left = 0
    Top = 0
    Width = 752
    Height = 21
    Align = alTop
    TabOrder = 0
    Text = 'https://map.google.com/'
    OnKeyPress = URLBoxKeyPress
  end
  object Panel1: TPanel
    Left = 0
    Top = 21
    Width = 752
    Height = 41
    Align = alTop
    BevelOuter = bvNone
    TabOrder = 1
    ExplicitLeft = 296
    ExplicitTop = 360
    ExplicitWidth = 185
    object Button1: TButton
      Left = 8
      Top = 8
      Width = 120
      Height = 25
      Caption = 'Show Dev Tools'
      TabOrder = 0
      OnClick = Button1Click
    end
    object UseResourceHandlerBox: TCheckBox
      Left = 152
      Top = 12
      Width = 233
      Height = 17
      Caption = 'Use Resource Handler'
      TabOrder = 1
    end
  end
end


unit MainUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, cefgui, ceflib, cefvcl, StdCtrls, ExtCtrls;

type
  TMainForm = class(TForm)
    URLBox: TEdit;
    Panel1: TPanel;
    Button1: TButton;
    UseResourceHandlerBox: TCheckBox;
    procedure FormCreate(Sender: TObject);
    procedure URLBoxKeyPress(Sender: TObject; var Key: Char);
    procedure Button1Click(Sender: TObject);
  private
    browser: TChromium;
    procedure BrowserGetResourceHandler(Sender: TObject;
        const Browser: ICefBrowser; const Frame: ICefFrame;
        const Request: ICefRequest; out Result: ICefResourceHandler);
  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}

uses
  WebInterceptHandler;

procedure TMainForm.URLBoxKeyPress(Sender: TObject; var Key: Char);
begin
  if Key = #13 then
  begin
    if UseResourceHandlerBox.Checked then
      browser.OnGetResourceHandler := BrowserGetResourceHandler
    else
      browser.OnGetResourceHandler := nil;

    browser.Load(URLBox.Text);
  end;
end;

procedure TMainForm.Button1Click(Sender: TObject);
begin
  browser.ShowDevTools;
end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
  browser := TChromium.Create(Self);
  browser.Align := alClient;
  browser.Parent := Self;
  //browser.Options.WebSecurity := STATE_DISABLED;
end;

procedure TMainForm.BrowserGetResourceHandler(Sender: TObject; const Browser:
    ICefBrowser; const Frame: ICefFrame; const Request: ICefRequest;
    out Result: ICefResourceHandler);
begin
  Result := TWebInterceptHandler.Create(Browser, Frame, 'webintercept', Request);
end;

end.

program DCEF3WebIntercept;

uses
  ceflib,
  Forms,
  MainUnit in 'MainUnit.pas' {MainForm},
  WebInterceptHandler in 'WebInterceptHandler.pas';

{$R *.res}

begin
  CefCache := 'cache';
  CefSingleProcess := False;
  if not CefLoadLibDefault then
    Exit;

  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TMainForm, MainForm);
  Application.Run;
end.

除了对这些问题的任何帮助外,欢迎任何改进此代码的建议。

此外,如果有人想知道,我打算稍后修改此代码,以便我可以更改响应。现在它只是GetResourceHandler的概念验证。

1 个答案:

答案 0 :(得分:2)

blob网址是一个特殊的网址,指的是您的浏览器当前在内存中为当前网页提供的数据。在我看来,在BrowserGetResourceHandler()中你应该检测url是否以&#34; blob开头:&#34;并在这种情况下返回NULL并让Chromium处理内部提取该URL。如果仍然不起作用,可能是CEF的限制,应报告错误。

CORS安全问题似乎是由请求/响应中缺少标头引起的。做一些调试:

  1. 在Google Chrome中检查相同请求的标题
  2. 在TWebInterceptHandler.GetResponseHeaders中检查headerMap参数中的标题,并确保它们与Google Chrome中的标题匹配
  3. 确保调用FResponse.SetHeaderMap()并检查响应上是否成功设置了标头 - 调用FResponse.GetHeaderMap并将其与headerMap参数进行比较