Delphi 10.1和CEF3与cookie有关

时间:2017-07-04 11:30:40

标签: delphi chromium-embedded

我有这段代码:

function VisitCookie(const name, value, domain, path: ustring;
  secure, httponly, hasExpires: Boolean; const creation, lastAccess,
  expires: TdateTime; Count, total: integer; out deleteCookie: Boolean)
  : Boolean;
begin
  RichEdit1.Lines.Add('cookie ' + inttostr(Count) + '/' + inttostr(total));
  RichEdit1.Lines.Add('name ' + name);
  RichEdit1.Lines.Add('value ' + value);
  RichEdit1.Lines.Add('domain ' + domain);
  RichEdit1.Lines.Add('path ' + path);
  RichEdit1.Lines.Add('secure ' + BoolToStr(secure));
  RichEdit1.Lines.Add('httponly ' + BoolToStr(httponly));
  RichEdit1.Lines.Add('hasExpires ' + BoolToStr(hasExpires));
  RichEdit1.Lines.Add('creation ' + DateToStr(creation));
  RichEdit1.Lines.Add('lastAccess ' + DateToStr(lastAccess));
  RichEdit1.Lines.Add('expires ' + DateToStr(expires));
  RichEdit1.Lines.Add('------------------------');
  Result := true;
end;

function GetCookies: Boolean;
begin
  CookieManager := TCefCookieManagerRef.Global(nil);
  CookieManager.VisitAllCookiesProc(VisitCookie);
end;

如果我在我的函数Result := false中设置VisitCookie - 我只获得第一个cookie的值,就是全部。即通过cookie不会发生。但是如果我设置Result := true - 我遇到了访问冲突,但它运行正常,直到我在Chromium中没有那么多cookie记录,例如5-10条记录。我不知道为什么会这样。

1 个答案:

答案 0 :(得分:0)

问题是VisitAllCookies方法的访问者回调函数是在CEF worker 线程的上下文中执行的,而不是在主线程的上下文中执行,因此您无法访问VCL从那里控制。 VisitAllCookies方法立即返回,然后从CEF worker 线程异步调用回调函数。

如何实施这种合作有很多方法。但它不是CEF特有的。它是关于如何从工作线程回调传递(或收集)某些数据并将其传递回主线程。也可选择以同步方式控制控制回调(以中断运行枚举)。

这是一个未经测试的例子(可能过于复杂)。原则仍然是,如何从主线程的不受控制的线程回调函数中收集数据(或以同步方式控制它):

type
  TCookie = record
    Name: string;
    Value: string;
    Expires: TDateTime;
  end;

  TProcessCookieEvent = procedure(Sender: TObject; const Cookie: TCookie;
    const IsLast: Boolean; var Delete: Boolean; var Cancel: Boolean) of object;

  TCookieManager = class
  private
    FWndHandle: HWND;
    FOnProcessCookie: TProcessCookieEvent;
  protected
    procedure WndProc(var Msg: TMessage); virtual;
  public
    constructor Create;
    destructor Destroy; override;
    procedure ProcessCookies(Timeout: UINT = 5000);
    property OnProcessCookie: TProcessCookieEvent read FOnProcessCookie write FOnProcessCookie;
  end;

implementation

const
  PA_CANCEL = 1;
  PA_DELETE = 2;
  CM_PROCESSCOOKIE = WM_USER + 100;

type
  PCookie = ^TCookie;

constructor TCookieManager.Create;
begin
  inherited;
  FWndHandle := AllocateHWnd(WndProc);
end;

destructor TCookieManager.Destroy;
begin
  DeallocateHWnd(FWndHandle);
  inherited;
end;

procedure TCookieManager.WndProc(var Msg: TMessage);
var
  Delete: Boolean;
  Cancel: Boolean;
  IsLast: Boolean;
begin
  if Msg.Msg = CM_PROCESSCOOKIE then
  begin
    Msg.Result := 0;

    if Assigned(FOnProcessCookie) then
    try
      Delete := False;
      Cancel := False;
      IsLast := Boolean(Msg.wParam);

      FOnProcessCookie(Self, PCookie(Msg.lParam)^, IsLast, Delete, Cancel);

      if Delete then
        Msg.Result := Msg.Result or PA_DELETE;
      if Cancel then
        Msg.Result := Msg.Result or PA_CANCEL;
    except
      Application.HandleException(Self);
    end;
  end
  else
    Msg.Result := DefWindowProc(FWndHandle, Msg.Msg, Msg.wParam, Msg.lParam);
end;

procedure TCookieManager.ProcessCookies(Timeout: UINT = 5000);
var
  CookieManager: ICefCookieManager;
begin
  CookieManager := TCefCookieManagerRef.Global(nil);
  CookieManager.VisitAllCookiesProc(

    { this function will be called asynchronously from a CEF worker thread }
    function(const Name, Value, Domain, Path: UString; Secure, HTTPOnly,
      HasExpires: Boolean; const Creation, LastAccess, Expires: TDateTime;
      Count, Total: Integer; out DeleteCookie: Boolean): Boolean
    var
      MsgRet: DWORD;
      Cookie: TCookie;
      IsLast: Boolean;
    begin
      { initialize cancel of enumeration and no cookie deletion }
      Result := False;
      DeleteCookie := False;

      { fill the cookie structure }
      Cookie.Name := string(Name);
      Cookie.Value := string(Value);
      Cookie.Expires := Expires;

      { determine if it's the last enumerated cookie }
      IsLast := Count = Total-1;

      { yes, I'm doing what many would not do, but let me explain, this is not
        SendMessage, that could get stuck forever when the message pump of the
        receiver got stucked so I've let this thread responsive (SMTO_NORMAL),
        let this call fail when the receiver is "hung" (SMTO_ABORTIFHUNG) and
        let the function fail if the receiver is destroyed (SMTO_ERRORONEXIT)
        and there is the timeout, in which the receiver needs to process this
        message (if the message is not processed for some reason, enumerating
        stops) }
      if SendMessageTimeout(FWndHandle, CM_PROCESSCOOKIE, WPARAM(IsLast),
        LPARAM(@Cookie), SMTO_NORMAL or SMTO_ABORTIFHUNG or SMTO_ERRORONEXIT,
        Timeout, MsgRet) <> 0 then
      begin
        Result := MsgRet and PA_CANCEL <> PA_CANCEL;
        DeleteCookie := MsgRet and PA_DELETE = PA_DELETE;
      end;
      { else GetLastError and try to signal error by posting another message }
    end;

  );
end;

可能的用法:

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    FCookieList: TList<TCookie>;
    FCookieManager: TCookieManager;
    procedure DoProcessCookie(Sender: TObject; const Cookie: TCookie;
      const IsLast: Boolean; var Delete: Boolean; var Cancel: Boolean);
  end;

implementation

procedure TForm1.FormCreate(Sender: TObject);
begin
  FCookieList := TList<TCookie>.Create;
  FCookieManager := TCookieManager.Create;
  FCookieManager.OnProcessCookie := DoProcessCookie;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FCookieManager.Free;
  FCookieList.Free;
end;

procedure TForm1.DoProcessCookie(Sender: TObject; const Cookie: TCookie;
  const IsLast: Boolean; var Delete: Boolean; var Cancel: Boolean);
begin
  { IsLast signals last enumerated cookie, Delete output parameter can delete
    the currently enumerated cookie, and Cancel output parameter can stop the
    enumeration }
  FCookieList.Add(Cookie);
  if IsLast then
    ShowMessage('All cookies has been enumerated!');
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  FCookieList.Clear;
  FCookieManager.ProcessCookies;
end;