我如何对一个changeAttributes有意义的反应:从WebView的代理传递? [英] How can I react meaningfully to a changeAttributes: delegation pass-through from WebView?
问题描述
WebView
通过 WebEditingDelegate
支持委托人为各种操作实现自定义行为的机制 WebView
(或私人 WebHTMLView
)接收。当一个动作如:
- (void)changeAttributes:(id)sender
在 WebHTMLView
中接收,它被传递到委托方法:
- (BOOL)webView:(WebView *)webView doCommandBySelector:(SEL)command
pre>
不幸的是,该机制并未规定在原始操作方法中传送
sender
。 / p>
对于绝大多数操作,发送者并不重要,但是对于changeAttributes和changeFont,合同要求
sender
由收件人调用以便例如
convertAttributes:
或convertFont:
。
code> changeFont case,结果是调用
[[NSFontManager sharedFontManager] convertFont:]
是足够的,巧合的是,这是什么
在
changeAttributes
案例中,特别是当删除线改变时,发送者可能是私有类NSFontEffectsBox
,其大概对应于字体面板中负责更改删除/设置的子部分。
不幸的是,调用
[[NSFontManager sharedFontManager] convertAttributes:]
不会获得预期的属性更改。这使得一个代表有兴趣在一个难题中有意义地实现这个方法:
WebKit不传达发送者,因此委托不能使合同
[sender convertAttributes:]
调用。
code> changeAttributes:调用发送到一个私有WebKit类,
WebHTMLView
,它不能被子类化,例如,自定义行为changeAttributes:
$ c> call,
NSFontEffectsBox
,是一个私人类,无法访问[NSFontEffectsBox sharedFontEffectsBox]
似乎没有办法让开发人员有意义地覆盖
changeAttributes: for
WebView
的行为。
任何想法?
解决方案这是一个邪恶的。一个合适的邪恶的动作(它们都不是特别干净或理想的)将是:
做一些内联汇编器回头看堆栈从堆栈中读取发送者参数(或调用者的调用者,视情况而定)。这当然假定当调用
WebHTMLView
时发送者被放置在栈上而不是在%eax
。
在
WebHTMLView $上添加类别c $ c>使用类似于
__ my_evil_hacky_nasty_ugly_changeAttributes_thing:
的方法,并在运行时使用objC运行时的method_exchangeImplementations()函数来交换你的类别的实现。你的方法变成changeAttributes:
,他们的变成__ my_evil_hacky_nasty_ugly_changeAttributes_thing:
,然后你可以调用传递原始调用。 / p>
正如我所说,两者都不是特别理想,但第二个优点是完全的运行时支持明确设计让你这样做),因为你在运行时查找类和方法,它是容错的。然而,在这种情况下,失败会让你回到方形。
真的,它需要一个错误记录对WebKit,让他们通过发送方,使其有意义。你的覆盖版本可能寻找一个方法
- (BOOL)webView:(WebView *)webView doCommandBySelector:(SEL)selector sender:(id)sender
,否则只是调用到原来的方法。这是苹果的代码应该做的,TBH。
WebView
supports, through theWebEditingDelegate
, a mechanism for the delegate to implement custom behavior for a variety of actions theWebView
(or the privateWebHTMLView
) 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:
orconvertFont:
.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 aWebView
.Any ideas?
解决方案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.这篇关于我如何对一个changeAttributes有意义的反应:从WebView的代理传递?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!