본문 바로가기
IT/델파이

Chromium 메시지 처리 매커니즘 이해 및 구현 방법

by 불멸남생 2024. 6. 25.

개요

Chromium에는 TWebBrowser 컴포넌트처럼 웹브라우저를 직접 핸들링하는 기능을 제공하지 않습니다. 따라서 VCL Application에서 Embedding된 Chromium 웹브라우저에 특정 작업을 시키려면(예: 웹페이지 HTML을 얻는 등) Chromium의 메시징 처리 매커니즘을 이해할 필요가 있습니다.

Chromium 기본 메시지 처리 매커니즘

Chromium 어플리케이션 초기화 및 메시지 처리 핸들러 지정

Chromium을 Embedding하는 VCL 어플리케이션에서는 최초 1회 다음과 같은 초기화 작업을 해야 합니다.

GlobalCEFApp := TCefApplication.Create;
반응형

그리고 GlobalCEFApp.OnProcessMessageReceived 메시지 핸들러에서 Chromium에 전달되는 모든 이벤트를 처리해야 합니다. 이 이벤트 핸들러는 TChromium 컴포넌트별로 수신된 메시지를 각각 처리할 수 있기 때문에, 메시지를 수신받은 브라우저 등의 메타 정보를 인식할 수 있습니다. 이 이벤트 핸들러의 함수 원형은 다음과 같습니다.

procedure GlobalCEFApp_OnProcessMessageReceived(const browser       : ICefBrowser;   
                                                const frame         : ICefFrame;     
                                                sourceProcess : TCefProcessId; 
                                                const message       : ICefProcessMessage; 
                                                var   aHandled      : boolean);

메시지 처리 핸들러를 다음과 같이 CEFApplication의 이벤트 핸들러로 등록해야 합니다.

GlobalCEFApp := TCefApplication.Create;
GlobalCEFApp.RemoteDebuggingPort := 9000;
GlobalCEFApp.OnProcessMessageReceived := GlobalCEFApp_OnProcessMessageReceived;
GlobalCEFApp.DisableFeatures := 'NetworkService,OutOfBlinkCors';
GlobalCEFApp.LogFile := 'debug.log';
GlobalCEFApp.LogSeverity := LOGSEVERITY_ERROR;
반응형

Chromium에 처리할 메시지 생성 및 전송

Chromium에서 사용되는 메시지는 TCefProcessMessageRef 클래스를 사용하여 생성 후 전송합니다. 이때 TChromium에 전달할 대상은 PID_RENDERER 이어야 합니다.

TempMsg := TCefProcessMessageRef.New('HTML얻기');
Chromium1.SendProcessMessage(PID_RENDERER, TempMsg);

Chromium Application 메시지 핸들러에서 VISITDom 객체 생성 및 실행

앞서 등록한 Chromium Application 메시지 핸들러에서 message.name에 따른 각 용도별 VisitDom 객체를 생성하고 VisitDom을 호출함으로써 원하는 작업을 웹브라우저에 시킬 수 있습니다. 이때 VisitDom 객체 생성 시 동작할 Callback 함수를 함께 전달해야 하는데, 이는 VisitDom 객체 내부적으로 visit 함수가 실행될 때 지정된 콜백 함수가 실행되기 때문입니다.

VisitDom 객체는 유닛 uCEFDomVisitor.pas에 선언된 TCefDomVisitorOwn 클래스를 상속받아 작성된 클래스를 사용할 수 있으며, 이미 기정의된 두 개의 TCefDomVisitorOwn 객체를 사용할 수도 있습니다.

반응형
TCefFastDomVisitor = class(TCefDomVisitorOwn)
  protected
    FProc : TCefDomVisitorProc;
    procedure visit(const document: ICefDomDocument); override;
  public
    constructor Create(const proc: TCefDomVisitorProc); reintroduce; virtual;
end;

TCefFastDomVisitor2 = class(TCefDomVisitorOwn)
  protected
    FProc : TCefDomVisitorProc2;
    FBrowser : ICefBrowser;
    FFrame : ICefFrame;
    procedure visit(const document: ICefDomDocument); override;
  public
    constructor Create(const browser: ICefBrowser; const frame: ICefFrame; const proc: TCefDomVisitorProc2); reintroduce; virtual;
    destructor Destroy; override;
end;

콜백 함수 타입인 TCefDomVisitorProc 또는 TCefDomVisitorProc2는 uCEFInterfaces.pas 유닛에 다음과 같이 정의되어 있습니다.

반응형
unit uCEFInterfaces;
...
TCefDomVisitorProc = {$IFDEF DELPHI12_UP}reference to{$ENDIF} procedure(const document: ICefDomDocument);
TCefDomVisitorProc2 = {$IFDEF DELPHI12_UP}reference to{$ENDIF} procedure(const browser : ICefBrowser; const frame: ICefFrame; const document: ICefDomDocument);

어플리케이션 이벤트 핸들러에서 메시지명에 따라 다음과 같이 VisitDOM을 호출할 수 있습니다.

반응형
procedure DOMVisitor_GETHTML(const browser: ICefBrowser; const frame: ICefFrame; const document: ICefDomDocument);
var
  msg: ICefProcessMessage;
begin
  msg := TCefProcessMessageRef.New(DOMVISITOR_MSGNAME_FULL);
  msg.ArgumentList.SetString(0, document.Body.AsMarkup);
  frame.SendProcessMessage(PID_BROWSER, msg);
end;

procedure DOMVisitor_GETTEXT(const browser: ICefBrowser; const frame: ICefFrame; const document: ICefDomDocument);
var
  msg: ICefProcessMessage;
begin
  msg := TCefProcessMessageRef.New(DOMVISITOR_MSGNAME_FULL);
  msg.ArgumentList.SetString(0, document.Body.AsText);
  frame.SendProcessMessage(PID_BROWSER, msg);
end;

procedure GlobalCEFApp_OnProcessMessageReceived(const browser       : ICefBrowser;
                                                const frame         : ICefFrame;
                                                sourceProcess : TCefProcessId;
                                                const message       : ICefProcessMessage;
                                                var   aHandled      : boolean);
begin
  aHandled := False;
  if (browser <> nil) then
  begin
    if (message.name = 'HTML얻기') then
    begin
      TempFrame := browser.MainFrame;
      if (TempFrame <> nil) then
      begin
        TempVisitor := TCefFastDomVisitor2.Create(browser, TempFrame, DOMVisitor_GETHTML);
        TempFrame.VisitDom(TempVisitor);
      end;
      aHandled := True;
    end
    else if (message.name = 'TEXT얻기') then
    begin
      TempFrame := browser.MainFrame;
      if (TempFrame <> nil) then
      begin
        TempVisitor := TCefFastDomVisitor2.Create(browser, TempFrame, DOMVisitor_GETTEXT);
        TempFrame.VisitDom(TempVisitor);
      end;
      aHandled := True;
    end
    else
      // 기타 메시지 처리
  end;
end;
반응형

VisitDOM 처리 결과 받기

VisitDOM 콜백 함수에 의해 실행된 결과 메시지를 보냈다면 TChromium.OnProcessMessageReceived 핸들러에서 메시지를 받을 수 있습니다.

procedure TDOMVisitorFrm.Chromium1ProcessMessageReceived(Sender: TObject;
  const browser: ICefBrowser; const frame: ICefFrame; sourceProcess: TCefProcessId;
  const message: ICefProcessMessage; out Result: Boolean);
begin
  Result := False;
  if (message = nil) or (message.ArgumentList = nil) then exit;
  if (message.Name = DOMVISITOR_MSGNAME_GETHTML) then
  begin
    ShowStatusText('DOM Visitor result text : ' + message.ArgumentList.GetString(0));
    Result := True;
  end
  else if (message.Name = DOMVISITOR_MSGNAME_GETTEXT) then
  begin
    ShowStatusText('DOM Visitor result text : ' + message.ArgumentList.GetString(0));
    Result := True;
  end
  else
    // 기타 메시지 처리
end;
반응형

결론

Chromium의 메시지 처리 매커니즘을 이해하고 이를 통해 VCL 어플리케이션 내에서 다양한 작업을 처리할 수 있습니다. 이를 위해 메시지 핸들러와 VisitDom 객체를 적절히 활용해야 합니다. 이러한 과정을 통해 웹브라우저와의 상호작용을 더욱 효과적으로 수행할 수 있습니다.

반응형