Enable WebGL in the Mac OS application on WebKit - objective-c

I am writing an application for Mac OS - browser on WebKit to use for the some site on WebGL. All is ready, the application correctly displays normal HTML sites, but WebGL doesn't work. How can enable WebGL in my app?

Once you have a WebView, there is an undocumented method which can enable WebGL:
WebPreferences *p = [webView preferences];
if ([p respondsToSelector:#selector(setWebGLEnabled:)]) {
[p setWebGLEnabled:YES];
}
In this example I have protected it with respondsToSelector: to ensure the code will not abort if later versions remove this specific option.
Note that I understand that an application containing code using undocumented interfaces may be rejected if submitted to Apple's Mac App Store.
Another option is to use a different embedded renderer which officially supports WebGL (where Apple's WebKit as demonstrated in Safari only has it as a developer option, presumably intended to be experimental). Since both Firefox and Chrome support WebGL, have a look at Gecko and Chromium Embedded Framework. (Note: I have not been able to confirm whether whether embedded Gecko supports WebGL.)

In Mavericks, this is the correct code.
WebPreferences *preferences = [_webview preferences];
if([preferences respondsToSelector:#selector(setWebGLEnabled:)]){
[preferences performSelector:#selector(setWebGLEnabled:) withObject:[NSNumber numberWithBool:YES]];
}
You need to call performSelector otherwise you'll get a compile error.

I figured out how to do this without using an undocumented method. You need to set the user preference WebKitWebGLEnabled to #YES for your app. To have the setting apply the first time the app is run, it needs to be set early, before the WebView is initialized. In my case, the WebView instance is loaded from the main nib file, so that's very early indeed. I added this code to Supporting Files/main.m:
[[NSUserDefaults standardUserDefaults] setObject:#YES
forKey:#"WebKitWebGLEnabled"];
Please note, since no undocumented method is called, I assume this code is within app store guidelines, but my app hasn't been reviewed yet!

Related

Write SDK version to binary when compiling from command line (macOS)

I am trying to detect whether the system is in dark mode.
I have already tried reading AppleInterfaceStyle from the user defaults i.e.
NSString *interfaceStyle = [[NSUserDefaults standardUserDefaults] stringForKey:#"AppleInterfaceStyle"];
BOOL isDark = [#"dark" caseInsensitiveCompare:interfaceStyle] == NSOrderedSame;
which works most of the time but has issues in Auto mode on Catalina.
Now from what I have read is that the more robust approach is to check the effectiveAppearance of NSApplication which looks like this:
NSApplication *app = [NSApplication sharedApplication];
NSAppearance *appearance = app.effectiveAppearance;
NSAppearanceName appearanceName = [appearance bestMatchFromAppearancesWithNames:#[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]];
BOOL isDark = [appearanceName isEqualToString:NSAppearanceNameDarkAqua];
The problem with this approach is that the application I am writing this for manually sets its appearance property, which prevents the effectiveAppearance from using the system appearance.
I tried settings app.appearance = nil before checking the effectiveAppearance but it didn't help.
Now there also is [NSAppearance currentAppearance] which uses the appearance of the current thread. I'm not quite sure what this value resolves to if the thread hasn't set the value explicitly.
My big problem here is that I have no access to a machine running macOS to check my code, so I would highly appreciate if someone knows what to do here.
Edit: It looks like the issue is that the library isn’t compile against the correct version of the SDK. Or at least that version isn’t written to the library information.
From the documentation:
If you build your app against an earlier SDK but still want to support Dark Mode, include the NSRequiresAquaSystemAppearance key (with a value of NO) in your app's Info.plist file. Do so only if your app's appearance looks correct when running in macOS 10.14 and later with Dark Mode enabled.
I am already specifying the version through -mmacosx-version-min=10.14. From what I have found this issue is basically the same that I have, but I don’t quite understand what the solution is from the commit.
I guess it has something to do with the -isysroot and -platform_version. But I didn’t find any good reference for what they do and how they work.
My updated question would be:
How do -isysroot and -platform_version work and how do I use them to enable SDK specific functionality with my binaries?
The solution is quite simple. When manually compiling from the command line -mmacosx-version-min=10.14 needs to get passed to the compiler and the linker.

Fetch preferences / permissions for NSUserNotificationCenter

I'm currently trying to set up a notification service using the old NSUserNotification API on macOSX (not iOS) in objective c++.
The catch is that I would need to know the system preferences associated with notifications that the user set (and preferably during the rest of runtime too) to determine if my notifications are actually seen. The application is meant to get the users attention in some way to guarantee things like reminders don't go unnoticed, so the thing I want to avoid is a notification delivered that silently doesn't produce any sound nor image on the screen whatsoever without the application knowing. If the notification is not allowed the application should handle it in some other way.
I am working on a macOS 10.14 Mojave machine. My overall code with NSUserNotificationCenter works fine with a custom Delegate that implements shouldDeliver, didActivate etc. Its just that I am currently assuming the user didn't press the "frick off" button on my notifications.
Unfortunately, NSUserNotificationCenter and related classes don't appear to feature a function that determines the permissions the way the new API (UNUserNotificationCenter) does. I have been trying to resolve this for a few hours so I was wondering if anyone else has found a solution to this. I can't really use the new API since this is strictly for back-compatibility in favour of the possibly rather prevalent amount of people who don't use mojave yet.
Things I have tried to make it work so far:
Try and see if there is a .plist somewhere where these settings are stored, in the User-specific Library folder as well as the general Library folder.
Try and find said .plist using CFPreferencesCopyKeyList and related methods. Apparently I don't really know the right domain name.
This answer from 2012 which appears to be outdated since I cannot locate the database on my machine anymore. Might be due to me using a mojave machine, or the file has since moved somewhere else.
Try and determine whether we can retro-actively check our permissions by checking a NSUserNotification's "presented" property on didDeliverNotification, shouldPresentNotification in the delegate. Unfortunately as stated in NSUserNotificationCenter itself it still behaves the same whether the preferences allow notifications or not.
Some code I tried includes the following:
CFPreferencesCopyKeyList((CFStringRef) #"com.apple.systemPreferences.plist", kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
CFPreferencesCopyKeyList((CFStringRef) #"com.apple.systemPreferences.plist", kCFPreferencesAnyUser, kCFPreferencesAnyHost);
//Unfortunately returns null with any combination of com.apple.notificationcenter.plist, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost
CFPreferencesCopyKeyList((CFStringRef) #"com.apple.notificationcenter", kCFPreferencesAnyUser, kCFPreferencesAnyHost);
On my system (only macOS High Sierra), it looks like the file you're after is:
~/Library/Preferences/com.apple.ncprefs.plist
That would correspond to a combination of kCFPreferencesCurrentUser, and kCFPreferencesAnyHost. (For the record, kCFPreferencesCurrentUser + kCFPreferencesCurrentHost equates to pref files in the ~/Library/Preferences/ByHost/ folder).
I don't think any settings would be stored in the local domain (by that I mean in the root /Library/Preferences/ folder).
I was able to get a list of apps and settings using the following code:
[[NSUserDefaults standardUserDefaults] addSuiteNamed:#"com.apple.ncprefs"];
NSArray *apps = [[NSUserDefaults standardUserDefaults] objectForKey:#"apps"];
NSLog(#"[%# %#] apps == %#", NSStringFromClass([self class]), NSStringFromSelector(_cmd), apps);
NOTE: this will not work if your app is sanboxed, as access to other pref files like that will be denied.

iOS access to Safari Reader feature through UIWebView

I am using iOS 4.3 & was wondering if there is any way that I can access the Safari's "Reader" feature through which webpages are removed of ads & other riff raff & the content takes the center stage.
If one opens any article in Safari (on say Wikipedia website), then a "Reader" button appears on the URL bar. Clicking on it presents a new window presenting the content beautifully.
How can I leverage this this functionality in iOS through UIWebView ?
PS: I know there is something called Readability Project. But I have no idea how to use this through UIWebView. Also for some websites Safari's Reader takes a call not to enable "Reader" feature, maybe it has no sufficient confidence?
Important: THIS ANSWER NO LONGER WORKS!
Readability shut down on September 30, 2016.
Here is something they recommend as a replacement:
https://mercury.postlight.com/web-parser/
Keeping the answer as a historical reference
--- Original answer ---
You can use Readability mobilizer for this. You will get a cleaned up version of any article, in the Readability styling:
http://www.readability.com/m?url=http://{URLOFTHEARTICLE}
Just prepare the URL and load it in your UIWebView. Here is how it looks in action:
http://www.readability.com/m?url=http%3A%2F%2Fwww.cnn.com%2F2013%2F01%2F11%2Fshowbiz%2Ftv%2Fgolden-globes-tv-vineyard%2Findex.html%3Fhpt%3Dhp_abar
Apple is making a pretty big deal about the inclusion of "Reader" in iOS 5. I'm assuming by the noise it's not available in 4.3
re: How to use through UIWebView
I can't find any mention of it in the Web Content Guide.
There's nothing about it in the UIWebView class reference.
And there's nothing in QA1630.
Dont parse HTML natively on iOS, I have done it before and its a messy business. Either create your own web service to do all the nasty work or look into using readability (readability.com) they provide an API.
There is also an open source ruby, python and php readability port that you can find here
https://github.com/iterationlabs/ruby-readability
https://github.com/gfxmonk/python-readability
http://code.fivefilters.org/p/php-readability/source/tree/master/
For you ruby enthusiasts, readability is also available as a gem, just google it.
Actually reader button do a bit of analysis where it parse the HTML Page and then it sees a clear body tag to parse. If that plugin is able to extract the exact body it will enable the reader button (My understanding from the readability source code). Now to implement the same for webview you just need to embed java script in your code (this java script is already available in the readability source code) and then you can achieve the same effect.
But I suspect the future plan from apple for the same. Because they can not just let anyone else do this content extraction with the huge business opportunity associated with iCloud with the combination of readability.
If you want you can simple extract the HTML from UIWebView and then extract the body and use it for your purpose. It's not a very rocket science to extract.
For analysis point of view, just have randomly some 10 HTML pages with Reader button enabled, you will see the core cotent belongs to body only and rest of the add, header, footer are separated.
I believe this is the time to re-invent the web content we use, and this is the perfect example of doing the same.
You can even do this by injecting javascript.
#define readJS #"(function(){window.baseUrl='https://www.readability.com';window.readabilityToken='';var s=document.createElement('script');s.setAttribute('type','text/javascript');s.setAttribute('charset','UTF-8');s.setAttribute('src',baseUrl+'/bookmarklet/read.js');document.documentElement.appendChild(s);})()"
And then when your webpage finishes loading
- (void)webViewDidFinishLoad:(UIWebView *)webview
{
[webview stringByEvaluatingJavaScriptFromString:readJS];
You can do it in iOS9.
first import SafariServices:
#import <SafariServices/SafariServices.h>
Afterwards we are instantiating SFSafariViewController and adding it as a subview. We have two options doing so:
Creating with only base URL
Creating with bas URL as well as entering 'Reading Mode' in case it is available
NSString *sURL = #"http://google.com";
NSURL *URL = [NSURL URLWithString:sURL];
SFSafariViewController *safari = [[SFSafariViewController alloc] initWithURL:URL]; // 1.
SFSafariViewController *safari = [[SFSafariViewController alloc] initWithURL:URL entersReaderIfAvailable:YES]; // 2.
[self presentViewController:safari animated:YES completion:nil];

Calling App Expose In Lion

Is there any way to call App Expose in Lion programmatically, for example on an event tap, etc?
If you don't mind using a TOTALLY UNDOCUMENTED API, which might change at any point without notice:
void CoreDockSendNotification(CFStringRef, void *);
(...)
CoreDockSendNotification(#"com.apple.expose.front.awake", NULL);
Other known arguments are #"com.apple.expose.awake" and #"com.apple.dashboard.awake", which activate Mission Control and Dashboard, respectively. #"com.apple.showdesktop.awake" used to activate Show Desktop, but no longer works on current versions of macOS.
Note that most applications should not use these calls -- these actions are intended to be invoked directly by the user.
Expose does not exist in Lion, it has been merged with Spaces into the Mission Control application.
You can launch Mission Control:
[[NSWorkspace sharedWorkspace] launchApplication:#"Mission Control"];

Prevent Flash in Cocoa WebView

In my Cocoa app, I want to prevent Flash from loading in a WebView, and let the user decide if the Flash should be shown for each page. (That's the same behavior already available through the ClickToFlash plugin or Safari extension. Bundling any of those extensions is probably not an option because of licensing issues.)
Unfortunately most of the Flash I'm trying to avoid is generated from embedded JavaScript specifically designed to prevent easy flash blocking, so I cannot filter the raw HTML for inclusion of Flash objects.
Also, I cannot disable JavaScript for my WebView, as the page I want to display looks completely different when JavaScript is turned off.
Is there a notification/hook I can use to modify the page DOM after JavaScript has been executed, but before the Flash plugin is loaded?
Or should I pursue a different direction?
Thanks,
Ilja
Ideally, you would just define your own WebKit plug-in that handles the application/shockwave-flash MIME type and make your plug-in do nothing.
However, there is unfortunately no way to control the priority of multiple WebKit plug-ins that all register for the same MIME type. The loading order of WebKit plug-ins is totally random and arbitrary, so you cannot guarantee that your plug-in will handle the Flash object instead of the Flash plug-in.
The only way around this that I've found is to subclass WebView and override the private method -_pluginForMIMEType: like so:
#class WebBasePluginPackage;
#interface WebView ( MyFlashPluginHack )
- (WebBasePluginPackage *)_pluginForMIMEType:(NSString *)MIMEType;
#end
#implementation MyWebView
- (WebBasePluginPackage *)_pluginForMIMEType:(NSString *)MIMEType
{
if ( [MIMEType isEqualToString:#"application/x-shockwave-flash"] )
{
return [super _pluginForMIMEType:#"application/my-plugin-type"];
}
else
{
return [super _pluginForMIMEType:MIMEType];
}
}
#end
Then you just need to create a custom WebKit plugin to handle "application/my-plugin-type" and have that plug-in do nothing at all.
Okay, we pretty much figured this out.
Since there is no official API that lets the host app know when JavaScript has finished or control what plugin should load, we are now using custom JavaScript that gets inserted into the received HTML we want to display.
The ClickToFlash Safari extension (not the Internet plugin, which it is based on) was a good inspiration.