How can I meaningfully respond to changeAttributes: delegating pass-through access from a WebView?

WebView supports, through WebEditingDelegate a delegation mechanism for implementing custom behavior for the various actions that WebView (or private WebHTMLView ) WebHTMLView . When an action such as:

 -(void)changeAttributes:(id)sender 

received in WebHTMLView , it is passed to the delegate method:

 -(BOOL)webView:(WebView *)webView doCommandBySelector:(SEL)command 

Unfortunately, the mechanism does not provide a 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 the sender called by the receiver, for example, convertAttributes: or convertFont:

In the case of changeFont it turns out that a call to [[NSFontManager sharedFontManager] convertFont:] is sufficient, since coincidentally this is what the sender is.

In the case of changeAttributes , in particular when changing strikethrough, the sender may be the private class " NSFontEffectsBox ", which presumably corresponds to the subsection of the font panel, which is responsible for changing the crossed out / etc settings.

Unfortunately, calling [[NSFontManager sharedFontManager] convertAttributes:] does NOT receive the expected attribute changes. This leaves the delegate who is interested in implementing this method largely in the hologram:

  • WebKit does not pass the sender, so the delegate cannot make the contract call [sender convertAttributes:] .

  • The call to changeAttributes: sent to the private WebKit class, WebHTMLView , which cannot be subclassed, for example, to configure the behavior of changeAttributes:

  • The sender for calling changeAttributes: NSFontEffectsBox is a private class and cannot be accessed, for example. like [NSFontEffectsBox sharedFontEffectsBox] .

In short: the developer does not make sense to significantly redefine the behavior of changeAttributes: for WebView .

Any ideas?

+8
fonts cocoa webkit
source share
2 answers

This evil. Accordingly, an evil couple of actions (none of them are especially pure or ideal):

  • Make some built-in assemblers to view the backup of the stack in order to read the sender argument from the stack of the caller (or the caller, as the case may be). This, of course, assumes that the sender is WebHTMLView stack, not %eax , when a call to WebHTMLView was made. However, this always applies to PowerPC code, so it probably does not have a starter.

  • Place the category in WebHTMLView using a method called __my_evil_hacky_nasty_ugly_changeAttributes_thing: and at run time use the exchange_ implementations () method from the __my_evil_hacky_nasty_ugly_changeAttributes_thing: runtime to exchange your category implementation using your own. Your method becomes changeAttributes: and their becomes __my_evil_hacky_nasty_ugly_changeAttributes_thing: after which you can call to transfer the original call.

As I said, none of them are particularly ideal, but the second has the advantage of full runtime support (i.e., the runtime is explicitly designed for this), and since you are viewing the class and methods at runtime, it is fault tolerant. However, failure in this case returns you to the square.

In fact, he needs a WebKit-related error to pass them to the sender in order to make it meaningful. Your overridden version could potentially look for a method -(BOOL)webView:(WebView*)webView doCommandBySelector:(SEL)selector sender:(id)sender and call it if it is found, otherwise just go to the original method. This is what Apple TBH code should do.

+4
source share

Have you looked at the source code?

WebHTMLView.mm

I do not see how -changeAttributes: calls -webView:doCommandBySelector: because inside this class it only calls its own method -doCommandBySelector:

 - (void)changeAttributes:(id)sender { [self _applyStyleToSelection:[self _styleForAttributeChange:sender] withUndoAction:EditActionChangeAttributes]; } - (void)doCommandBySelector:(SEL)aSelector { … if (![[webView _editingDelegateForwarder] webView:webView doCommandBySelector:aSelector] && coreFrame) { … } 

Also, why can't you subclass WebHTMLView? Is this due to API restrictions for the Mac App Store? Is WebKit considered private? I thought it was Open Source.

-Wil

+3
source share

Source: https://habr.com/ru/post/650032/


All Articles