How to render HTML from an NPAPI plugin in Safari - cocoa

I've been writing a Mac NPAPI-based browser plugin to convert a file of custom mimetype (say, "application/x-foo") into an HTML representation, which can then be viewed directly in a browser. These files are usually directly served, so I'm more concerned about supporting full-page viewing, rather than embedded views via <object> tags.
On Firefox, this has been relatively simple: I make a call to NPN_NewStream with the text/html mimetype, write the converted HTML into the stream, and then clean it up with NPN_DestroySteam. The browser automatically handles the request for a new HTML stream and renders the given HTML into a tab or window. Pretty standard.
For Safari, though, NPN_NewStream does not appear to be implemented (and I did check the WebKit source code). Previously, I was able to use the WebKitPlugin API. With Safari 5.1, this API is gone.
I thought I would be able to create a WebView in a drawing event handler, like this:
NSRect rect = NSMakeRect(0, 0, obj->window.width, obj->window.height);
//...
WebView* webView = [[WebView alloc] initWithFrame:rect frameName:nil groupName:nil];
[[webView mainFrame] loadHTMLString:#"<html><head><title>This is a message from my plug-in!</title></head><body><p><strong>This is a message from my plug-in!</strong></p></body>/html>" baseURL:[NSURL URLWithString:#"http://example.com"]];
[webView drawRect:rect];
and see it in browser. But all that does is render a gray screen with no content, as if not drawn. If I replace the WebView with an NSTextView and set its string to the HTML, everything draws just fine, but of course the HTML is not rendered by an NSTextView.
My question boils down to: is there a good way to render some HTML into a Safari window from an NPAPI plugin? Or if that won't work, into a Google Chrome window? Or some other approach that lets me handle a custom MIME type and display some HTML representation of it?

Honestly, I'm surprised that your newstream approach works on Firefox; that's a new one I've never heard of. If you have to do the drawing yourself, you are limited to either using CoreGraphics with a CGContext or CoreAnimation with a CALayer. In other words, you don't get a NSView, so you can't directly render a WebView into it.
That said, you could put the webview in an offscreen context and render it to your CGContext, then proxy the events; this seems a little complicated for what you're trying to do, but I don't know of another way to do it.
There is an experimental library in FireBreath that does this; scroll wheel isn't implemented, but the other events work, including mouse drag and keyboard. There are still a few strange things with it when using form events, so whether or not this would work for you depends on what content you'll be displaying.
If you're interested in trying to use FireBreath for this you can drop into the FireBreath chat room during business hours (GMT-0600) and I'm usually around and can discuss it with out.

Related

High Sierra WebView blocks anchor tag navigation

I have a Mac app that displays info in a WebView. Quite an old app now and in Objective-C which I am rapidly forgetting.
The WebView contains anchor tags to allow navigation within the pages, both by clicking on links in the page displayed, or by clicking tabs in the UI which send Javascript to the WebView. In High Sierra this no longer works although it displays no error.
It appears that I need to implement isKeyExcludedFromWebScript: but my attempts to do so have failed.
Do I need to sub-class WebView? Putting isKeyExcludedFromWebScript: and isSelectorExcludedFromWebScript: in the View Controller containing the WebView doesn't work - they never get called.
If anyone has any advice or examples, I would be most grateful.
I am afraid that the answer will be to upgrade the app to use WKWebView but I was hoping for a quick work-around until I get time to do that.
Answering my own question here as I have worked out a solution.
The problem was that I was loading an HTML string into the WebView and when I tried to use internal navigation links, it didn't have a base URL to use as a prefix and so the anchor navigation never worked.
It used to work, prior to High Sierra, so there must be something new about how WebViews operate.
The solution was to save the HTML string to a temporary file and have the WebView load that file's URL instead of loading the string directly.
This applies to both WebView and WKWebView.

Webkit page scaling in Cocoa

I'm making a WebKit-based text editor app for Mac, and I need to find a way to zoom the document in and out.
I've seen this StackOverflow article here, it suggests just scaling the view that Webkit is rendered into. The problem is, since Webkit doesn't know about it, this breaks things like drag & drop and causes Javascript to report the wrong cursor locations. Unfortunately, for some reason, the Cocoa WebKit API only supports scaling text up or down, not the whole page.
Safari and Chrome are both able to do this properly. I've gone as far as to look through the Chromium source code to figure out how it's done, but unfortunately it's using a completely different cross-platform API.
Any advice would be greatly appreciated. I'm not opposed to using private APIs, if I can figure out how to use them in a safe way.
-Keaton
The APIs for doing this aren't currently public, but they've been around for years and aren't likely to change. You can find them in WebKit's WebViewPrivate.h header. You won't find that header on your system, so you'll have to redeclare those methods yourself in a category. Something like:
#interface WebView (Zoom)
- (IBAction)zoomPageIn:(id)sender;
- (IBAction)zoomPageOut:(id)sender;
- (IBAction)resetPageZoom:(id)sender;
#end
Another option is just to set the CSS zoom property on the html element in your editor. You can do that in markup (<html style="zoom: 1.5">) or in JavaScript (document.documentElement.style.zoom = "1.5"). That doesn't require using any private APIs.

WebBrowser div scrolling

If I limit WebBrowser's height, and content is longer, scrollbar is created inside the WebBrowser (effectively, HTML page is long), similar to IE in WP7. This scrollbar behaves the way I want to - after swift swipe it continues to scroll, slowing down.
But if I place a div with a scrollbar in the WebBrowser content, this scrollbar behaves like regular HTML scrollbar, it does not keep scrolling after user stops touching the display.
Is there a way to make this work too ?
Thanks
No, not easily. The WP7 IE9 browser control supports the overflow:scroll CSS property that allows the user to scroll content within the current page. It is worth noting that most Android and iPhone browsers do not support this, although it has been added in iOS5. None of these browser support inertia scrolling for overflow:scroll, WP7 included.
The only way to get inertia scrolling for overflow content is to do it yourself using JavaScript. For WebKit browsers there is a popular library, iScroll, that provides this functionality. However, it does not work for WP7 (due to lack of CSS3 3D transforms that are used to push the scrolling content onto a different layer for hardware acceleration) - so you will need to write your own implementation. Good luck!

Safari 5.1 npapi issue

Since several days I am trying to resolve the folowing issue, reading all I found around the web about npapi on mac.
The goal is to have a npapi plugin which works for safari and firefox(mac).
My software (that I can not rewrite specialy for this purpose hase about 45000 lines of C code) is based on a NSView attached to a NSDocument....
I have a webkit version based plugin that I must trash (thanks to Apple!) based to the same NSView.
I have a npapi version plugin which works fine on firefox. In this npapi plugin, I take the carbon window ref, I make a NSWindow based on that:
NSWindow *browserWindow = [[[NSWindow alloc] initWithWindowRef:wind]autorelease];
and I put my NSView on this window and that works.
Now the pb is that I can not do the same thing on safari.
Look at attached picture, the window is not in the safari's window!
I tryed several ways... it dose not work.
Can a cocoa's gourou says where I am making something wrong? or is this a known issue?
NPError NPP_SetWindow(NPP instance, NPWindow* window){
NP_CGContext *ctx = window->window;
void *wind = ctx->window;
...
in the NSView init function:
NSWindow *browserWindow = [[NSWindow alloc] initWithWindowRef:wind];
self = [super initWithFrame:frame];
if( self )
{
[browserWindow makeFirstResponder: self];
[self setNextResponder: nil];
[browserWindow setContentView:self];
[self webPlugInInitialize];// my own initializing
}
return self;
In Safari 5.1, the web rendering is not done by Safari itself, but on a different process to enhance security. Open up the Activity Monitor, and you see that background process called "Safari Web Process" or something like that.
So, you can't and shouldn't create NSWindow based on the Carbon window ref which can be obtained within NPAPI plugin.
Read Apple's own documentation on this point. You should request the core graphics drawing method, and then the WindowRef field of NP_CGContext should have a NSWindow*, not the Carbon window ref.
If it works on Firefox, that's totally shocking and completely unsupported. Does it work in Firefox 4 and later?
If you absolutely have to use an NSView, the only way that I know to do it in a plugin is to render the NSView into your CGContext. Keep in mind that in newer NPAPI browsers with the Cocoa event system you get the CGContextRef as part of the draw event; to request a draw event you can call NPN_InvalidateWindow.
FireBreath has a completely experimental and not-fully-functional example of rendering an NSView (ans specifically a WebView) into a CGContextRef that you could look at as an example.
Other than using a CGContextRef your only other choice is to use a CALayer; if you can find a way to make a NSWindow or NSView in that you could be okay, but I don't know if there is one. Someone suggested that setting the CALayer as the rendering layer for the NSView might work. Either way you'll most likely have to forward all the events since you are basically hosting the NSView in an offscreen view.
Make no mistake; there is no supported way to get an NSView in the browser. There never has been -- methods that people have used were unsupported and depended on browser-specific implementations of the API. When you use things like that, you can reliably expect them to eventually break, such as in this case. For more information on the drawing models, you could read Stuart Morgan's blog post on the subject, check out the FireBreath mac drawing model docs, or read the Cocoa event model spec.
Given that you start with "take the carbon window ref", your approach is doomed, because it is based on the Carbon event model (and not just that, but assumptions about its internal implementation details). Anyone running Firefox on a 64-bit system will have to manually restart Firefox in 32-bit mode for your hack to work, and even then it will only work until Firefox completely removes Carbon support (which is planned for the foreseeable future).
As the other answers said, where you are going wrong is that your whole approach is completely unsupported, and the fact that it ever worked at all as an NPAPI plugin was luck. You simply cannot use an NSView directly in an NPAPI plugin.

How do I scroll my Palm webOS application?

This feels like a dumb newbie question. I'm attempting to follow the Palm's online tutorial for developing a webOS app. I ran palm-generate and tried updating the generated index.html file to have a body with a bunch of content (the word "HELLO" repeated 400 times).
When I attempt to load that document in the emulator, I find that I'm unable to scroll the screen to read the rest of the text. I drag the screen, but nothing happens. The page scrolls just fine in the Palm web browser.
Is there a setting to allow users to drag my app to scroll it? I see documentation for a Mojo.Widget.Scroller, but that seems like it shouldn't be necessary for a simple web page with a bunch of text on it.
Thanks to arthurthornton:
You can take anything that would normally be inside the tags and put that inside the scene HTML file.
OR you could just make a basic scene with a webview widget inside it that has your site as the source.
Unfortunately, the Scroller widget is needed for scrolling. The standard WebKit view that's shown in a card has no native scrolling ability, so the Mojo scroller widget implements the logic for displaying more content that can fit.
You can try this out by making a new project in the Ares IDE. Just add a Scroller and use the Maximize button to make it fill the scene. Then, add a HTML object to it, and add your HTML content to that object. You'll then have a nice scene which allows you to scroll through a lot of content.

Resources