BYOB: Build Your Own Browser, Part 3
Pages: 1, 2
The apply and windowDidBecomeKey methods are inverses
of each other; apply sets values into WebPreferences and MyDocument
and windowDidBecomeKey looks up values in WebPreferences and MyDocument.
One odd thing that happens is that we setAutosaves every time to
YES in the WebPreferences object, p. This is not strictly
necessary, but we do this to ensure that the preferences are being saved, on
the off chance that the value has changed since the last time we accessed it.
Here is the code for apply and windowDidBecomeKey:
- (IBAction)apply:(id)sender
{
NSUserDefaults *defaults;
defaults = [NSUserDefaults standardUserDefaults];
NSString* dHomepage = [defaultHomepage stringValue];
[defaults setObject:dHomepage forKey:@"defaultHomepage"];
[md setDefaultHomepage:dHomepage];
NSString* iContent = [ignoreContent stringValue];
[defaults setObject:iContent forKey:@"ignoreContent"];
[md setIgnoreContent:iContent];
[p setAutosaves:YES];
if ([autoloadImages state] == NSOffState){
[p setLoadsImagesAutomatically:NO];
}
else {
[p setLoadsImagesAutomatically:YES];
}
if ([allowAnimatedImages state] == NSOffState){
[p setAllowsAnimatedImageLooping:NO];
}
else {
[p setAllowsAnimatedImageLooping:YES];
}
if ([javascriptEnabled state] == NSOffState){
[p setJavaScriptEnabled:NO];
}
else {
[p setJavaScriptEnabled:YES];
}
}
- (void)windowDidBecomeKey:(NSNotification *)aNotification
{
[defaultHomepage setStringValue:[md getDefaultHomepage]];
[ignoreContent setStringValue:[md getIgnoreContent]];
[p setAutosaves:YES];
if ([p loadsImagesAutomatically]){
[autoloadImages setState:NSOnState];
}
else {
[autoloadImages setState:NSOffState];
}
if ([p allowsAnimatedImageLooping]){
[allowAnimatedImages setState:NSOnState];
}
else {
[allowAnimatedImages setState:NSOffState];
}
if ([p isJavaScriptEnabled]){
[javascriptEnabled setState:NSOnState];
}
else {
[javascriptEnabled setState:NSOffState];
}
}
You probably noticed that we made use of the NSUserDefaults classes
in all three methods to save the information for default homepage. NSUserDefaults
allows developers to save information into the user's defaults database that
can be reused between instance of the program. This is very convenient and makes
it so that developers do not need to make awkward preferences files that are
lost if they are not in a specific location.
While I don't go into details about using this here, NSUserDefaults
is well documented on Apple's
developer site. You may also want to check out the defaults
command to see what the defaults are for this and other applications.
Once all the code is written and saved, you're done. When you run the code, you should see that preferences first come up with the default values that you set in Interface Builder. But if you change them, quit and run the browser again; the values you entered on the preference pane should be saved. Adding additional preference variables, or other variables that might be interesting for you (for instance, the value for our Content Eliminator) is pretty straightforward. Build up the interface in Interface Builder and then connect the pieces by adding to each of the methods in Controller.
Content Eliminator
The final feature we're going to add to our browser is a content eliminator. Put simply, a content eliminator will prevent any content from being downloaded if its URL contains certain substrings. For instance, let's say you're sick of the number of banner ads that are on the pages you read. You still want to be able to download the pages that the ads are on, but you don't want to see the ads. If the ads come from a site named singlebutton.com, you would add that string to the content eliminator's list of strings to avoid, and it will prevent the content from being downloaded.
This feature can be used to prevent downloading from ad sites, as a child-guard-type system, or can even be reversed, to only allow content from certain URLs to be used in a kiosk-type application. If you are planning to use this in a kiosk-type application, make sure that you are very careful with the way that you check the strings, or else you may end up with people accessing yoursite.porno-site.com instead of yoursite.com.
To add a content eliminator to our browser, we need to use another of WebView's
delegates, this time the policyDelegate. The policyDelegate
allows an external application to make decisions about content, specifically
how it is to be downloaded and how it is to be displayed. There are several
methods that this delegate provides for policy decisions that need to be made.
The method that we will be using to make policy decisions is:
decidePolicyForNavigationAction:(NSDictionary *)actionInformation
request:(NSURLRequest *)request
frame:(WebFrame *)frame
decisionListener:(id<WebPolicyDecisionListener<)listener
When this method is called by WebView, WebView sends it information about the request being made in several objects, and a listener whose job is to report back to WebView what the method has decided to do about the request. When we implement the method, our code needs to comb through the information that is provided about the request and tell the listener what policy decision was made based on that information.
Before we get too far, the first thing that we need for our content eliminator
is to tell the WebView what the policyDelegate is going to be.
We do this the same way we did with the UIDelegate and the frameDelegate,
by adding:
[webView setPolicyDelegate:self];
to the windowControllerDidLoadNIB method.
Once that is added, we need a static list of sites from which we do not want
content to be downloaded. To accomplish this, we are going to use a semi-colon
delimited static NSString named ignoreContent. Since
our NSString is static, we need to declare it in the MyDocuments
implementation file, MyDocument.m, as we did our defaultHomepage
variable:
static NSString *ignoreContent = @"site1.com; site2.com;site3.com";
Now we need to build a decidePolicyActionForNavigation method
that does four things:
- Figure out what the URL is.
- Convert the semi-colon delimited list into an array.
- Go through the array and decide if the content needs to be eliminated; if so, tell the listener to ignore it.
- If nothing needs to be eliminated, then let the content go through.
The implementation of the method follows these parameters:
- (void)webView:(WebView *)sender decidePolicyForNavigationAction:
(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:
(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener
{
NSString* urlKey = [[actionInformation objectForKey:WebActionOriginalURLKey] host];
if(ignoreContent == nil){
[listener use];
return;
}
if (urlKey == nil){
[listener ignore];
return;
}
NSArray* theList = [ignoreContent componentsSeparatedByString:@";"];
int count = [theList count];
while(count > 0 ){
NSRange range = [urlKey rangeOfString:[theList objectAtIndex:--count]];
if(range.length >0){
[listener ignore];
return;
}
}
[listener use];
}
Once the method has been added, test it out and see if you can block some
content or even entire sites. While I left it out of the article, if you follow
the instructions in the preferences section, the ignoreContent
string is one of the variables that can be added to both the user default database
and the preferences window. While this is a simple of example of what a policy
delegate can do, policy delegates can be used for any number of interesting
filtering-type applications
Final Thoughts
As you can see, WebKit provides the custom browser developer with loads of features that can be used in custom browsers, kiosk applications, browsers built in to other applications, or, really, whatever browsing application you can imagine. Hopefully, you will be inspired to create cool applications that really test the bounds of what WebKit can do.
Good luck developing your browser! Drop us a line if you find any interesting links or if you create any interesting WebKit-based projects.
Andrew Anderson is a software developer and consultant in Chicago who's been using and programming on the Mac as long as he can remember.
Return to the Mac DevCenter
-
Unknown variables
2008-07-09 04:41:58 Dev_Dave [View]
-
For all you askting
2008-04-09 20:52:08 MaxApps [View]
-
Tabbed browser
2008-03-08 10:15:49 Abhi1907 [View]
-
Display Page Title
2007-07-07 13:29:24 ALEPETY [View]
-
Setting URL Textveiw to current
2006-04-07 17:55:43 rcran [View]
-
Errors
2006-01-30 21:41:36 something2sea [View]
-
Downloading support
2006-01-28 15:45:56 something2sea [View]
-
WebKit References?
2005-12-20 11:15:17 Tipo61 [View]
-
Error addition
2005-12-03 22:16:01 cuddlespowell [View]
-
Error
2005-12-03 22:06:48 cuddlespowell [View]
-
Adding Search Fields
2005-06-30 18:43:57 Abcu [View]
-
Adding Search Fields
2006-12-07 16:56:29 tominated [View]
-
Adding Search Fields
2006-12-07 16:55:52 tominated [View]
-
6 Warnings and 4 Errors prevent Compiling
2005-02-28 13:35:33 ratpack [View]
-
6 Warnings and 4 Errors prevent Compiling
2005-05-22 20:07:39 Danys [View]
-
preferences not saving
2005-02-27 10:21:53 pack [View]
-
Displaying an Error warning
2004-11-21 17:27:33 Shizil [View]
-
Prefrences
2004-11-12 15:27:04 Nemisis [View]
-
Prefrences
2004-11-13 03:22:15 Nemisis [View]
-
https support
2004-08-04 11:53:36 Steve.Haun [View]
-
https support
2004-08-04 16:44:16 Steve.Haun [View]
-
Lost...
2004-08-04 09:28:02 Zarac [View]
-
Lost...
2004-08-05 05:55:19 Andrew Anderson | [View]
-
Running in another computer
2004-08-02 16:17:08 aceM [View]
-
Running in another computer
2005-01-19 07:12:12 tf23 [View]
-
Running in another computer
2004-08-05 05:51:07 Andrew Anderson | [View]
-
Thanks for the great articles
2004-06-25 18:02:16 Beowulf [View]
-
Just remember why I hate C and his friends
2004-06-12 14:12:21 zeus [View]
-
Not so good...
2004-06-07 07:18:30 johnts [View]
-
re: Not so good...
2004-06-07 07:59:29 Andrew Anderson | [View]
-
re: Not so good...
2004-06-07 18:48:29 johnts [View]
-
re: Not so good...
2004-06-07 19:50:11 Andrew Anderson | [View]
-
Problem Closing Windows
2005-08-01 09:06:13 Roalkl [View]
-
re: Not so good...
2004-06-07 22:01:47 johnts [View]
-
re: Not so good...
2004-06-08 05:17:58 Andrew Anderson | [View]
-
re: Not so good...
2004-06-08 20:39:53 johnts [View]
-
re: Not so good...
2004-06-07 18:28:11 johnts [View]

