• 0
serser

Использование WebView. Отслеживание переходов с помощью decidePolicyForNavigationAction

Вопросы

Помогите портировать интерфейсы для реализации делегата для WebView.setPolicyDelegate, с помощью которого можно запрещать или переопределять переходы по ссылкам.
Отталкивался от хелпа эпла. Реализовал вроде как все, что нужно. Страница гугла открывается, но колбэк делегата не вызывается.

unit MainFormUnit;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,

  Macapi.AppKit,
  Macapi.CocoaTypes,
  Macapi.Foundation,
  Macapi.ObjectiveC, FMX.StdCtrls,

  Posix.Stdlib, Macapi.CoreFoundation, FMX.Platform.Mac,
  System.Generics.Collections;

type
  WebFrameClass = interface(NSObjectClass)
  ['{7BE750C8-DFEC-4870-851A-12DBCB0B78F6}']
  end;

  WebFrame = interface(NSObject)
  ['{BCFA04BE-41AB-4B78-89C0-3330F12C7695}']
    procedure loadRequest(request: NSURLRequest); cdecl;
  end;

  TWebFrame = class(TOCGenericImport<WebFrameClass, WebFrame>)
  end;

  WebViewClass = interface(NSViewClass)
  ['{0D9F44B7-09FD-4E35-B96E-8DB71B9A2537}']
    {class} function canShowMIMEType(MIMEType: NSString): Boolean; cdecl;
  end;

  WebView = interface(NSView)
  ['{C36D8016-2FCB-49F0-BA1C-C9913A37F9AC}']
    procedure clos; cdecl;
    procedure setPolicyDelegate(delegate: Pointer); cdecl;
    procedure setHostWindow(hostWindow: NSWindow); cdecl;
    function initWithFrame(frame: NSRect; frameName: NSString; groupName: NSString): Pointer; cdecl;
    function mainFrame: WebFrame; cdecl;
  end;

  TWebView = class(TOCGenericImport<WebViewClass, WebView>)
  end;

  WebPolicyDecisionListener = interface(IObjectiveC)
  ['{2AD8355D-6C57-410D-A4F4-230C90B6D799}']
    procedure download; cdecl;
    procedure ignore; cdecl;
    procedure use; cdecl;
  end;

  WebViewDelegate = interface(IObjectiveC)
  ['{5B66FF5F-DA92-48D3-A715-C89A68AA8995}']
    procedure decidePolicyForNavigationAction(actionInformation: NSDictionary;
      request: NSURLRequest; frame: WebFrame; listener: WebPolicyDecisionListener); cdecl;
  end;

  TWebViewDelegate = class(TOCLocal, WebViewDelegate)
    procedure decidePolicyForNavigationAction(actionInformation: NSDictionary;
      request: NSURLRequest; frame: WebFrame; listener: WebPolicyDecisionListener); cdecl;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
  public
    MyWebView: WebView;
    MyDelegate: TWebViewDelegate;
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.FormCreate(Sender: TObject);
var
  PWebView: Pointer;
  FwkMod: HMODULE;
  urlStr: NSURL;
  urlreq: NSURLRequest;

  MyView: NSView;
const
  WebKitFWK: string = '/System/Library/Frameworks/WebKit.framework/WebKit';
begin
  FwkMod := System.SysUtils.LoadLibrary(PWideChar(WebKitFWK));
  PWebView := TWebView.Alloc.initWithFrame(MakeNSRect(10, 10, 500, 500), nil, nil);
  MyWebView := TWebView.Wrap(PWebView);
  MyWebView.setAutoresizingMask(NSViewHeightSizable or NSViewWidthSizable);
  //MyWebView.setHostWindow(MyNSWindow);

  MyView := WindowHandleToPlatform(Form1.Handle).View;

  MyView.addSubview(MyWebView);

  MyDelegate := TWebViewDelegate.Create;
  MyWebView.setPolicyDelegate((MyDelegate as ILocalObject).GetObjectID);

  urlStr := TNSURL.Wrap(TNSURL.Alloc.initWithString(NSSTR('http://www.google.com/')));
  urlreq := TNSURLRequest.Wrap(TNSURLRequest.Alloc.initWithURL(urlstr));
  MyWebView.mainFrame.loadRequest(urlreq);

  urlStr.release;
  urlreq.release;
end;

{ TWebViewDelegate }

procedure TWebViewDelegate.decidePolicyForNavigationAction(
  actionInformation: NSDictionary; request: NSURLRequest; frame: WebFrame;
  listener: WebPolicyDecisionListener);
begin
  MessageDlg('hi', TMsgDlgType.mtInformation, [TMsgDlgBtn.mbOK], 0);
end;

end.

Также не совсем понятно, где должен располагаться метод webView:decidePolicyForNavigationAction:request:frame:decisionListener. В документации он объявлен как:

- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id < WebPolicyDecisionListener>)listener

Т.е. похоже на то, что он должен быть методом класса WebView? Или же методом произвольного класса делегата?

 

И еще, я так понимаю, что полное имя этого метода webView:decidePolicyForNavigationAction:request:frame:decisionListener, то есть объявляя его как decidePolicyForNavigationAction возможно мы лишаем mac os возможности найти его. Но как же тогда объявить его полностью?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

6 ответов на этот вопрос

  • 0

Я могу только в понедельник проверить.

 

Но есть предположение. Добавьте конструктор по умолчанию в TWebViewDelegate и внутри вызовите базовый конструктор TOCLocal.

 

Методы протокола делегата должен быть в интерфейсе WebViewDelegate 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Вы имеете ввиду вот так?) 

constructor TWebViewDelegate.Create;
begin
  inherited Create;
end;

Это какой-то хак? Попробовал, но ничего не изменилось(

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Ошибка в неправильной трансляции методов протокола. В вашем случае:

– webView:decidePolicyForNavigationAction:request:frame:decisionListener:

Должен перейти в (Правильно):

procedure webView(webView1: WebView; decidePolicyForNavigationAction: NSDictionary; request: NSURLRequest; frame: WebFrame; decisionListener: WebPolicyDecisionListener); cdecl;

А не в (Неправильно), потеряли параметр webView :

procedure decidePolicyForNavigationAction(actionInformation: NSDictionary; request: NSURLRequest; frame: WebFrame; listener: WebPolicyDecisionListener); cdecl;

В качестве примера, посмотрите для сравнения, как транслируется CLLocationManagerDelegate:

– locationManager:didFailWithError:
– locationManager:didUpdateToLocation:fromLocation:

В Delphi:

  CLLocationManagerDelegate = interface(IObjectiveC)
    ['{C1794A16-F4FB-46E1-839E-2AFBA43B420C}']
    procedure locationManager(manager: CLLocationManager; didFailWithError: NSError); cdecl; overload;
    procedure locationManager(manager: CLLocationManager; didUpdateToLocation: CLLocation; fromLocation: CLLocation); cdecl; overload;
  end;

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

О чудо! Оно работает) Спасибо, Ярослав!

 

Я думал, что связывание происходит только по имени метода.

 

Для полной ясности хотелось бы еще уточнить, потому что не очень понятен принцип перевода.

Первый параметр (webView1 для которого, кстати, не важно имя) является Self-ом, который передается неявно в ObjC, но у нас он выделен явно? В этом случае можно ли использовать дельфевый Self? (Скорее всего можно, но все же)

 

Все-таки что означает первая часть определения, до имени метода:

- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id < WebPolicyDecisionListener>)listener

То, что webView является первым параметром, а webView: началом имени или есть какой-то еще смысл?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
  • Первая часть (webView) - название сообщения.
  • Остальное это параметры.

На счет наименования, я не проверял. Но по-моему связь идет по типу параметров, а не по названию. Поэтому, если встречаются два сообщения Objective C с одинаковыми списками типов, но разными параметрами, то в Delphi нужно такие методы помечать атрибутами.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Нет-нет, сейчас проверил. Связывается только по имени всех параметров, кроме первого и по имени метода. Причем попробовал тип WebFrame в объявлении заменить на NSURL и делегат все равно вызвался. Спасибо)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти

  • Последние посетители   0 пользователей онлайн

    Ни одного зарегистрированного пользователя не просматривает данную страницу