如何对来自 WebView 的changeAttributes: delegate pass-through做出有意义的反应?
WebView
通过 WebEditingDelegate
支持一种委托机制,可以为 WebView
(或私有 >WebHTMLView
) 接收。当 WebHTMLView
中接收到诸如以下的操作时
-(void)changeAttributes:(id)sender
,它会被传递到委托方法:
-(BOOL)webView:(WebView *)webView doCommandBySelector:(SEL)command
不幸的是,该机制不提供“sender
”在独创的动作方法。
对于绝大多数操作,发送者并不重要,但对于changeAttributes 和changeFont 来说,合约要求接收者调用“sender
”,以便例如convertAttributes:
或convertFont:
。
对于 changeFont
情况,事实证明,调用 [[NSFontManager sharedFontManager] ConvertFont:]
就足够了,巧合的是,这就是发送者。
在 changeAttributes
情况下,特别是当更改删除线时,发送者可能是一个私有类“NSFontEffectsBox
”,它可能对应于负责的字体面板的子部分更改删除线/等设置。
不幸的是,调用 [[NSFontManagersharedFontManager]convertAttributes:]
无法获得预期的属性更改。这使得有兴趣有意义地实现此方法的委托人陷入了一个难题:
WebKit 不传达发送者,因此委托人无法制定合同
[sender ConvertAttributes:]
调用。changeAttributes:
调用被发送到私有 WebKit 类WebHTMLView
,该类不能进行子类化,例如自定义changeAttributes:
的行为。 code>.changeAttributes:
调用的发送方NSFontEffectsBox
是一个私有类,无法以[NSFontEffectsBox sharedFontEffectsBox]
等方式进行访问。< /p>
简而言之:开发人员似乎无法有意义地覆盖 WebView
的 changeAttributes:
行为。
有什么想法吗?
WebView
supports, through the WebEditingDelegate
, a mechanism for the delegate to implement custom behavior for a variety of actions the WebView
(or the private WebHTMLView
) receives. When an action such as:
-(void)changeAttributes:(id)sender
is received in WebHTMLView
, it is passed through to the delegate method:
-(BOOL)webView:(WebView *)webView doCommandBySelector:(SEL)command
Unfortunately, the mechanism does not provide for conveyance of the "sender
" in the original action method.
For the vast majority of actions, the sender is unimportant, but for changeAttributes, and changeFont, for example, the contract requires that "sender
" be called by the recipient in order to e.g. convertAttributes:
or convertFont:
.
For the changeFont
case, it turns out that calling [[NSFontManager sharedFontManager] convertFont:]
is sufficient, as coincidentally this is what the sender is.
In the changeAttributes
case, in particular when strikethrough is changed, the sender may be a private class "NSFontEffectsBox
" which presumably corresponds to the subsection of the font panel that is responsible for changing strikethrough/etc settings.
Unfortunately, calling [[NSFontManager sharedFontManager] convertAttributes:]
does NOT obtain the expected attribute changes. This leaves a delegate who is interested in implementing this method meaningfully in a bit of a conundrum:
WebKit does not convey the sender, so the delegate can't make the contractual
[sender convertAttributes:]
call.The
changeAttributes:
call is sent to a private WebKit class,WebHTMLView
, which cannot be subclassed to, e.g., customize the behavior ofchangeAttributes:
.The sender for the
changeAttributes:
call,NSFontEffectsBox
, is a private class and cannot be accessed e.g. as[NSFontEffectsBox sharedFontEffectsBox]
.
In short: there appears to be no way for a developer to meaningfully override the behavior of changeAttributes:
for a WebView
.
Any ideas?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这是一种邪恶的行为。一对适当的邪恶操作(它们都不是特别干净或理想)将是:
执行一些内联汇编程序来查找堆栈以从调用者的堆栈(或调用者的调用者,视情况而定)中读取发送者参数是)。当然,这是假设在调用
WebHTMLView
时,发送方位于堆栈上,而不是位于%eax
中。然而,这将始终适用于 PowerPC 代码,因此它可能不会启动。在
WebHTMLView
上放置一个类别,其名称类似于__my_evil_hacky_nasty_ugly_changeAttributes_thing:
,并在运行时使用 ObjC 运行时中的 method_exchangeImplementations() 将您的类别的实现与他们的实现交换。您的方法变为changeAttributes:
,他们的方法变为__my_evil_hacky_nasty_ugly_changeAttributes_thing:
,然后您可以调用该方法来传递原始调用。正如我所说,两者都不是特别理想,但第二个具有完整运行时支持的优势(即运行时明确设计为让您执行此操作),并且由于您在运行时查找类和方法,所以它是失败的 -宽容。然而,在这种情况下失败会让你回到原点。
实际上,它需要针对 WebKit 记录一个错误,让它们传递给发件人,使其变得有意义。您的重写版本可能会查找方法
-(BOOL)webView:(WebView*)webView doCommandBySelector:(SEL)selector sender:(id)sender
并在找到时调用该方法,否则只需调用原来的方法。老实说,这就是苹果代码应该做的事情。This is an evil one. A suitably evil pair of actions (neither of them particularly clean or ideal) would be:
Do some inline assembler to look back up the stack to read the sender argument from the caller's stack (or the caller's caller, as the case should be). This of course assumes that the sender is placed on the stack and not in
%eax
when the call toWebHTMLView
was made. That will always apply to PowerPC code however, so it's likely a non-starter there.Put a category on
WebHTMLView
with a method named something like__my_evil_hacky_nasty_ugly_changeAttributes_thing:
and at runtime use method_exchangeImplementations() from the ObjC runtime to swap your category's implementation with theirs. Your method becomeschangeAttributes:
and theirs becomes__my_evil_hacky_nasty_ugly_changeAttributes_thing:
, which you can then call to pass on the original call.As I said, neither is particularly ideal, but the second has the advantage of full runtime support (i.e. the runtime is explicitly designed to let you do this), and since you're looking up the class and methods at runtime, it's failure-tolerant. Failure in this case gets you back to square one however.
Really it needs a bug logged against WebKit to have them pass on the sender to make it meaningful at all. Your overridden version could potentially look for a method
-(BOOL)webView:(WebView*)webView doCommandBySelector:(SEL)selector sender:(id)sender
and call that if found, otherwise just call through to the original method. This is what Apple's code should be doing, TBH.你看过源代码吗?
WebHTMLView.mm
我不明白
-changeAttributes:
正在调用-webView:doCommandBySelector:
,因为在此类中,它仅在其自己的-doCommandBySelector:
方法中调用。另外,为什么不能继承 WebHTMLView 呢?是因为Mac App Store对API的限制吗? WebKit 算私有吗?我以为它是开源的。
-威尔
Have you looked at the source code?
WebHTMLView.mm
I don't see how
-changeAttributes:
is calling-webView:doCommandBySelector:
, as within this class it's only called inside its own-doCommandBySelector:
method.Also, why can't you subclass WebHTMLView? Is it because of the Mac App Store restrictions on API? Does WebKit count as private? I thought it was Open Source.
-Wil