Перейти к содержанию
Fire Monkey от А до Я
  • 0

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


serser

Вопрос

Помогите портировать интерфейсы для реализации делегата для 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 возможности найти его. Но как же тогда объявить его полностью?

Ссылка на комментарий

Рекомендуемые сообщения

  • 0
  • Администраторы

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

 

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

 

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

Ссылка на комментарий
  • 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 и делегат все равно вызвался. Спасибо)

Ссылка на комментарий

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить на вопрос...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...