2010-12-10 8 views
8

WebView admite, a través del WebEditingDelegate, un mecanismo para que el delegado implemente un comportamiento personalizado para una variedad de acciones que recibe el WebView (o el WebHTMLView privado). Cuando una acción como:¿Cómo puedo reaccionar de manera significativa a un changeAttributes: paso de delegación desde WebView?

-(void)changeAttributes:(id)sender 

es recibida en WebHTMLView, se pasa a través con el método delegado:

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

Desafortunadamente, el mecanismo no proporciona para el transporte de la "sender" en el método de acción original.

Para la gran mayoría de las acciones, el remitente no es importante, pero para changeAttributes y changeFont, por ejemplo, el contrato requiere que "sender" sea llamado por el destinatario para, p. convertAttributes: o convertFont:.

Para el caso changeFont, resulta que llamar al [[NSFontManager sharedFontManager] convertFont:] es suficiente, ya que casualmente este es el remitente.

En el caso changeAttributes, en particular cuando se cambia el tachado, el remitente puede haber una clase privada "NSFontEffectsBox" que presumiblemente corresponde a la subsección del panel de la fuente que es responsable de cambiar tachado/etc ajustes.

Desafortunadamente, al llamar al [[NSFontManager sharedFontManager] convertAttributes:] NO se obtienen los cambios de atributos esperados. Esto deja un delegado que esté interesado en la aplicación de este método de manera significativa en un poco de un enigma:

  1. WebKit no transmite el emisor, por lo que el delegado no puede hacer el contrato [sender convertAttributes:] llamada.

  2. La llamada changeAttributes: se envía a una clase privada WebKit, WebHTMLView, que no puede ser una subclase de, por ejemplo, personalizar el comportamiento de changeAttributes:.

  3. El emisor de la llamada changeAttributes:, NSFontEffectsBox, es una clase privada y no se puede acceder, p. como [NSFontEffectsBox sharedFontEffectsBox].

En resumen: no parece haber ninguna manera para que un desarrollador para anular de manera significativa el comportamiento de changeAttributes: para un WebView.

¿Alguna idea?

+0

Un buen ejemplo de cómo hacer una pregunta. –

+0

¿Crees que podrías publicar rastros de pila de los diferentes escenarios? –

Respuesta

4

Esta es una maldad. Un par adecuadamente el mal de las acciones (ninguno de ellos particularmente limpia o ideal) sería:

  1. Haga un poco de ensamblador en línea para mirar hacia atrás en la pila para leer el argumento remitente de la pila de la persona que llama (o persona que llama de la persona que llama, como debería ser el caso). Por supuesto, esto supone que el emisor se coloca en la pila y no en %eax cuando se realizó la llamada al WebHTMLView. Sin embargo, eso siempre se aplicará al código de PowerPC, por lo que es probable que no arranque allí.

  2. Deja una categoría en WebHTMLView con un método llamado algo así como __my_evil_hacky_nasty_ugly_changeAttributes_thing: y al uso method_exchangeImplementations tiempo de ejecución (tiempo de ejecución) de la ObjC para intercambiar aplicación de su categoría con la de ellos. Su método se convierte en changeAttributes: y el suyo se convierte en __my_evil_hacky_nasty_ugly_changeAttributes_thing:, que luego puede llamar para pasar la llamada original.

Como ya he dicho, no es particularmente ideal, pero el segundo tiene la ventaja de soporte de ejecución completa (es decir, el tiempo de ejecución está explícitamente diseñado para dejar de hacer esto), y ya que estamos mirando hacia arriba y la clase métodos en tiempo de ejecución, es tolerante a fallas. Sin embargo, el fracaso en este caso te lleva de vuelta al punto de partida.

Realmente necesita un error registrado contra WebKit para hacer que pase el remitente para que sea significativo en absoluto. Su versión invalidada podría potencialmente buscar un método -(BOOL)webView:(WebView*)webView doCommandBySelector:(SEL)selector sender:(id)sender y llamar a eso si se encuentra, de lo contrario simplemente llame al método original. Esto es lo que el código de Apple debería estar haciendo, TBH.

+0

Gracias por la respuesta. Esperaba evitar el enfoque vertiginoso, ya que no estoy seguro de qué riesgo me pone en la medida en que Apple App Store prohibe la "API privada". – danielpunkass

+0

No debe haber riesgo de swizzling. Lo he usado en aplicaciones de iOS sin problemas. Ni siquiera ejecutan 'cadenas' en los binarios, por lo NSClassFromString (@" WebHTMLView ") pasa desapercibido. El tiempo de ejecución de ObjC también es público, así que puedes usar cualquier cosa allí. Lo uso para intercambiar métodos usando '-performSelectorOnMainThread:' con versiones 'dispatch_async()' en tiempo de ejecución en un grupo de clases también, aunque solo en mis propias clases –

+0

FWIW Finalmente informé sobre este error: http://www.openradar.me/radar?id=4965931952373760 Me tomó tanto tiempo porque dejé que el problema subyacente se quedara en el camino. Creo que me moveré en el corto plazo mientras espero una solución más duradera en el futuro. – danielpunkass

3

¿Has mirado el código fuente?

WebHTMLView.mm

no veo cómo -changeAttributes: está llamando -webView:doCommandBySelector:, ya que dentro de esta clase sólo se llama dentro de su propia -doCommandBySelector: método.

- (void)changeAttributes:(id)sender 
{ 
    [self _applyStyleToSelection:[self _styleForAttributeChange:sender] withUndoAction:EditActionChangeAttributes]; 
} 


- (void)doCommandBySelector:(SEL)aSelector 
{ 
… 
    if (![[webView _editingDelegateForwarder] webView:webView doCommandBySelector:aSelector] && coreFrame) { 
… 
} 

también, por qué no se puede subclase WebHTMLView? ¿Es debido a las restricciones de la App Store de Mac sobre la API? ¿WebKit cuenta como privado? Pensé que era de código abierto.

-Wil

+0

Wil, no sé qué versión de la fuente es. En la última versión troncal que estoy viendo, muchos de esos métodos de acción en WebHTMLView tienen una macro en la parte superior, "COMMAND_PROLOGUE", que se ocupa del envío del mensaje al delegado. En cuanto a la subclasificación de WebHTMLView, existe el hecho de que es una API privada, pero también que WebView no expone el acceso a los detalles de implementación de la vista de documento para la que se utiliza WebHTMLView. En cuanto a que WebKit sea de código abierto, no creo que eso importe. Apple quiere evitar dependencias privadas contra el sistema. – danielpunkass

Cuestiones relacionadas