Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

708 lines (626 sloc) 18.766 kb
//
// BrowserPane.m
// Vienna
//
// Created by Steve on 9/7/05.
// Copyright (c) 2004-2005 Steve Palmer. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "ViennaApp.h"
#import "BrowserPane.h"
#import "TabbedWebView.h"
#import "Constants.h"
#import "AppController.h"
#import "Preferences.h"
#import "HelperFunctions.h"
#import "StringExtensions.h"
#import "AddressBarCell.h"
#import <WebKit/WebKit.h>
#import "RichXMLParser.h"
@implementation BrowserPaneButtonCell
-(BOOL)isOpaque
{
return NO;
}
-(NSColor *)highlightColorInView:(NSView *)controlView
{
return nil;
}
@end
@implementation BrowserPaneButton
-(BOOL)isOpaque
{
return NO;
}
+(Class)cellClass
{
return [BrowserPaneButtonCell class];
}
-(NSColor *)highlightColorInView:(NSView *)controlView
{
return nil;
}
@end
@interface BrowserPane (Private)
-(void)endFrameLoad;
-(void)showRssPageButton:(BOOL)showButton;
-(void)setError:(NSError *)newError;
@end
@implementation BrowserPane
+ (void)load
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (self == [BrowserPane class]) {
//These are synonyms
[self exposeBinding:@"isLoading"];
[self exposeBinding:@"isProcessing"];
[self setKeys:[NSArray arrayWithObject:@"isLoading"] triggerChangeNotificationsForDependentKey:@"isProcessing"];
}
[pool release];
}
/* initWithFrame
* Initialise our view.
*/
-(id)initWithFrame:(NSRect)frame
{
if (([super initWithFrame:frame]) != nil)
{
controller = nil;
isLoading = NO;
isLocalFile = NO;
viewTitle = nil;
openURLInBackground = NO;
pageFilename = nil;
lastError = nil;
rssPageURL = nil;
}
return self;
}
/* awakeFromNib
* Do things that only make sense once the NIB is loaded.
*/
-(void)awakeFromNib
{
// Create our webview
[webPane initTabbedWebView];
[webPane setUIDelegate:self];
[webPane setFrameLoadDelegate:self];
[webPane setApplicationNameForUserAgent:[NSString stringWithFormat:MA_DefaultUserAgentString, [((ViennaApp *)NSApp) applicationVersion]]];
// Make web preferences 16pt Arial to match Safari
[[webPane preferences] setStandardFontFamily:@"Arial"];
[[webPane preferences] setDefaultFontSize:16];
// Use an AddressBarCell for the address field which allows space for the
// web page image and an optional lock icon for secure pages.
AddressBarCell * cell = [[[AddressBarCell alloc] init] autorelease];
[cell setEditable:YES];
[cell setDrawsBackground:YES];
[cell setBordered:YES];
[cell setBezeled:YES];
[cell setScrollable:YES];
[cell setTarget:self];
[cell setAction:@selector(handleAddress:)];
[cell setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]];
[addressField setCell:cell];
// Initialise address field
[addressField setStringValue:@""];
// The RSS page button is hidden by default
[self showRssPageButton:NO];
// Set tooltips
[addressField setToolTip:NSLocalizedString(@"Enter the URL here", nil)];
[refreshButton setToolTip:NSLocalizedString(@"Refresh the current page", nil)];
[backButton setToolTip:NSLocalizedString(@"Return to the previous page", nil)];
[forwardButton setToolTip:NSLocalizedString(@"Go forward to the next page", nil)];
[rssPageButton setToolTip:NSLocalizedString(@"Subscribe to the feed for this page", nil)];
}
/* setController
* Sets the controller used by this view.
*/
-(void)setController:(AppController *)theController
{
controller = theController;
[webPane setController:controller];
}
/* viewLink
* Return the URL being displayed as a string.
*/
-(NSString *)viewLink
{
if ([[[webPane mainFrame] dataSource] unreachableURL])
return [[[[webPane mainFrame] dataSource] unreachableURL] absoluteString];
return [[self url] absoluteString];
}
/* showRssPageButton
* Conditionally show or hide the RSS page button.
*/
-(void)showRssPageButton:(BOOL)showButton
{
[rssPageButton setEnabled:showButton];
}
/* setError
* Save the most recent error instance.
*/
-(void)setError:(NSError *)newError
{
[newError retain];
[lastError release];
lastError = newError;
}
/* handleAddress
* Called when the user hits Enter on the address bar.
*/
-(IBAction)handleAddress:(id)sender
{
NSString * theURL = [addressField stringValue];
// If no '.' appears in the string, wrap it with 'www' and 'com'
if (![theURL hasCharacter:'.'])
theURL = [NSString stringWithFormat:@"www.%@.com", theURL];
// If no schema, prefix http://
if ([theURL rangeOfString:@"://"].location == NSNotFound)
theURL = [NSString stringWithFormat:@"http://%@", theURL];
// cleanUpUrl is a hack to handle Internationalized Domain Names. WebKit handles them automatically, so we tap into that.
NSURL *urlToLoad = cleanedUpAndEscapedUrlFromString(theURL);
if (urlToLoad != nil)
[self loadURL:urlToLoad inBackground:NO];
else
[self activateAddressBar];
}
-(void)setViewTitle:(NSString *) newTitle
{
[newTitle retain];
[viewTitle release];
viewTitle = newTitle;
}
/* activateAddressBar
* Put the focus on the address bar.
*/
-(void)activateAddressBar
{
[[NSApp mainWindow] makeFirstResponder:addressField];
}
/* loadURL
* Load the specified URL into the web frame.
*/
-(void)loadURL:(NSURL *)url inBackground:(BOOL)openInBackgroundFlag
{
[self setViewTitle:@""];
openURLInBackground = openInBackgroundFlag;
isLocalFile = [url isFileURL];
[pageFilename release];
pageFilename = [[[[url path] lastPathComponent] stringByDeletingPathExtension] retain];
[addressField setStringValue:[url absoluteString]];
[[webPane mainFrame] loadRequest:[NSURLRequest requestWithURL:url]];
}
/* setStatusText
* Called from the webview when some JavaScript writes status text. Echo this to
* our status bar.
*/
-(void)webView:(WebView *)sender setStatusText:(NSString *)text
{
if ([[controller browserView] activeTabItemView] == self)
[controller setStatusMessage:text persist:NO];
}
/* mouseDidMoveOverElement
* Called from the webview when the user positions the mouse over an element. If it's a link
* then echo the URL to the status bar like Safari does.
*/
-(void)webView:(WebView *)sender mouseDidMoveOverElement:(NSDictionary *)elementInformation modifierFlags:(unsigned int)modifierFlags
{
NSURL * url = [elementInformation valueForKey:@"WebElementLinkURL"];
[controller setStatusMessage:(url ? [url absoluteString] : @"") persist:NO];
}
/* didStartProvisionalLoadForFrame
* Invoked when a new client request is made by sender to load a provisional data source for frame.
*/
-(void)webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame
{
if (frame == [webPane mainFrame])
{
[[controller browserView] setTabItemViewTitle:self title:NSLocalizedString(@"Loading...", nil)];
[self showRssPageButton:NO];
[self setError:nil];
[self setViewTitle:@""];
}
}
/* didCommitLoadForFrame
* Invoked when data source of frame has started to receive data.
*/
-(void)webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame
{
if (frame == [webPane mainFrame])
{
if (!isLoading)
{
[self willChangeValueForKey:@"isLoading"];
isLoading = YES;
[self didChangeValueForKey:@"isLoading"];
}
if (!openURLInBackground)
[[sender window] makeFirstResponder:sender];
// Show or hide the lock icon depending on whether this is a secure
// web page. Also shade the address bar a nice light yellow colour as
// Camino does.
NSURL * theURL = [[[frame dataSource] request] URL];
if ([[theURL scheme] isEqualToString:@"https"])
{
[[addressField cell] setHasSecureImage:YES];
[addressField setBackgroundColor:[NSColor colorWithDeviceRed:1.0 green:1.0 blue:0.777 alpha:1.0]];
[lockIconImage setHidden:NO];
}
else
{
[[addressField cell] setHasSecureImage:NO];
[addressField setBackgroundColor:[NSColor whiteColor]];
[lockIconImage setHidden:YES];
}
if (![[frame dataSource] unreachableURL])
[addressField setStringValue:[theURL absoluteString]];
else
[addressField setStringValue:[[[frame dataSource] unreachableURL] absoluteString]];
}
}
/* didFailProvisionalLoadWithError
* Invoked when a location request for frame has failed to load.
*/
-(void)webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
{
if (frame == [webPane mainFrame])
{
// Was this a feed redirect? If so, this isn't an error:
if (![webPane isFeedRedirect] && ![webPane isDownload])
{
[self setError:error];
// Use a warning sign as favicon
[iconImage setImage:[NSImage imageNamed:@"folderError.tiff"]];
// Load the localized verion of the error page
NSString * pathToErrorPage = [[NSBundle bundleForClass:[self class]] pathForResource:@"errorpage" ofType:@"html"];
if (pathToErrorPage != nil)
{
NSString *errorMessage = [NSString stringWithContentsOfFile:pathToErrorPage encoding:NSUTF8StringEncoding error:NULL];
errorMessage = [errorMessage stringByReplacingOccurrencesOfString: @"$ErrorInformation" withString: [error localizedDescription]];
if (errorMessage != nil)
{
[frame loadAlternateHTMLString:errorMessage baseURL:[NSURL fileURLWithPath:pathToErrorPage isDirectory:NO] forUnreachableURL:[[[frame provisionalDataSource] request] URL]];
}
NSString *unreachableURL = [[[frame provisionalDataSource] unreachableURL] absoluteString];
if (unreachableURL != nil)
[addressField setStringValue: [[[frame provisionalDataSource] unreachableURL] absoluteString]];
}
}
[self endFrameLoad];
}
}
/* endFrameLoad
* Handle the end of a load whether or not it completed and whether or not an error
* occurred. The error variable is nil for no error or it contains the most recent
* NSError incident.
*/
-(void)endFrameLoad
{
if (viewTitle == @"")
{
if (lastError == nil)
[[controller browserView] setTabItemViewTitle:self title:pageFilename];
}
[self willChangeValueForKey:@"isLoading"];
isLoading = NO;
[self didChangeValueForKey:@"isLoading"];
openURLInBackground = NO;
}
/* didFailLoadWithError
* Invoked when a location request for frame has failed to load.
*/
-(void)webView:(WebView *)sender didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
{
if (frame == [webPane mainFrame])
{
// Not really an error. A plugin is grabbing the URL and will handle it
// by itself.
if (!([[error domain] isEqualToString:WebKitErrorDomain] && [error code] == WebKitErrorPlugInWillHandleLoad))
{
[self setError:error];
// Use a warning sign as favicon
[iconImage setImage:[NSImage imageNamed:@"folderError.tiff"]];
// Load the localized verion of the error page
NSString * pathToErrorPage = [[NSBundle bundleForClass:[self class]] pathForResource:@"errorpage" ofType:@"html"];
if (pathToErrorPage != nil)
{
NSString *errorMessage = [NSString stringWithContentsOfFile:pathToErrorPage encoding:NSUTF8StringEncoding error:NULL];
errorMessage = [errorMessage stringByReplacingOccurrencesOfString: @"$ErrorInformation" withString: [error localizedDescription]];
if (errorMessage != nil)
{
[frame loadAlternateHTMLString:errorMessage baseURL:[NSURL fileURLWithPath:pathToErrorPage isDirectory:NO] forUnreachableURL:[[[frame dataSource] request] URL]];
}
}
}
[self endFrameLoad];
}
}
/* didFinishLoadForFrame
* Invoked when a location request for frame has successfully; that is, when all the resources are done loading.
*/
-(void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
{
if (frame == [webPane mainFrame])
{
// Once the frame is loaded, trawl the source for possible links to RSS
// pages.
NSData * webSrc = [[frame dataSource] data];
NSMutableArray * arrayOfLinks = [NSMutableArray array];
if ([RichXMLParser extractFeeds:webSrc toArray:arrayOfLinks])
{
[rssPageURL release];
rssPageURL = [arrayOfLinks objectAtIndex:0];
if (![rssPageURL hasPrefix:@"http:"])
rssPageURL = [[self viewLink] stringByAppendingString:rssPageURL];
[rssPageURL retain];
[self showRssPageButton:YES];
}
[self endFrameLoad];
}
}
/* didReceiveTitle
* Invoked when the page title arrives. We use this to set the tab title.
*/
-(void)webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame
{
if (frame == [webPane mainFrame])
{
[[controller browserView] setTabItemViewTitle:self title:title];
[self setViewTitle:title];
}
}
/* didReceiveIcon
* Invoked when we get the page icon.
*/
-(void)webView:(WebView *)sender didReceiveIcon:(NSImage *)image forFrame:(WebFrame *)frame
{
if (frame == [webPane mainFrame])
{
[image setScalesWhenResized:YES];
[image setSize:NSMakeSize(14, 14)];
[iconImage setImage:image];
}
}
/* createWebViewWithRequest
* Called when the browser wants to create a new webview.
*/
-(WebView *)webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request
{
[controller openURL:[request URL] inPreferredBrowser:YES];
// Change this to handle modifier key?
// Is this covered by the webView policy?
return nil;
}
/* setFrame
* Trap this to stop scripts from resizing the main Vienna window.
*/
-(void)webView:(WebView *)sender setFrame:(NSRect)frame
{
}
/* webViewClose
* Handle scripting closing a window by just closing the tab.
*/
-(void)webViewClose:(WebView *)sender
{
[[controller browserView] closeTabItemView:self];
}
/* contextMenuItemsForElement
* Creates a new context menu for our web pane.
*/
-(NSArray *)webView:(WebView *)sender contextMenuItemsForElement:(NSDictionary *)element defaultMenuItems:(NSArray *)defaultMenuItems
{
NSURL * urlLink = [element valueForKey:WebElementLinkURLKey];
if (urlLink != nil)
return [controller contextMenuItemsForElement:element defaultMenuItems:defaultMenuItems];
WebFrame * frameKey = [element valueForKey:WebElementFrameKey];
if (frameKey != nil && !isLocalFile)
return [controller contextMenuItemsForElement:element defaultMenuItems:defaultMenuItems];
return defaultMenuItems;
}
/* printDocument
* Print the web page.
*/
-(void)printDocument:(id)sender
{
[webPane printDocument:sender];
}
/* mainView
* Return the view that typically receives focus
*/
-(NSView *)mainView
{
return webPane;
}
/* webView
* Return the web view of this view.
*/
-(WebView *)webView
{
return webPane;
}
/* performFindPanelAction
* Implement the search action. Search the web page for the specified
* text.
*/
-(void)performFindPanelAction:(int)actionTag
{
switch (actionTag)
{
case NSFindPanelActionSetFindString:
{
[webPane searchFor:[controller searchString] direction:YES caseSensitive:NO wrap:YES];
break;
}
case NSFindPanelActionNext:
[webPane searchFor:[controller searchString] direction:YES caseSensitive:NO wrap:YES];
break;
case NSFindPanelActionPrevious:
[webPane searchFor:[controller searchString] direction:NO caseSensitive:NO wrap:YES];
break;
}
}
/* url
* Return the URL of the page being displayed.
*/
-(NSURL *)url
{
NSURL * theURL = nil;
WebDataSource * dataSource = [[webPane mainFrame] dataSource];
if (dataSource != nil)
{
theURL = [[dataSource request] URL];
}
else
{
NSString * urlString = [addressField stringValue];
if (urlString != nil)
theURL = [NSURL URLWithString:urlString];
}
return theURL;
}
-(NSString *)viewTitle
{
return viewTitle;
}
/* canGoForward
* Return TRUE if we can go forward to a web page.
*/
-(BOOL)canGoForward
{
return [webPane canGoForward];
}
/* canGoBack
* Return TRUE if we can go to a previous web page.
*/
-(BOOL)canGoBack
{
return [webPane canGoBack];
}
/* handleGoForward
* Go to the next web page.
*/
-(IBAction)handleGoForward:(id)sender
{
[webPane goForward];
}
/* handleGoBack
* Go to the previous web page.
*/
-(IBAction)handleGoBack:(id)sender
{
[webPane goBack];
}
/* swipeWithEvent
* Enables "back"/"forward" and "scroll to top"/"scroll to bottom" via three-finger swipes as in Safari and other applications.
*/
-(void)swipeWithEvent:(NSEvent *)event
{
CGFloat deltaX = [event deltaX];
CGFloat deltaY = [event deltaY];
// If the horizontal component of the swipe is larger, the user wants to go back or forward...
if (fabsf(deltaX) > fabsf(deltaY))
{
if (deltaX != 0)
{
if (deltaX > 0)
[self handleGoBack:self];
else
[self handleGoForward:self];
}
}
// Otherwise, she wants to go to the top/bottom of the page.
else
{
if (deltaY != 0)
{
if (deltaY > 0)
[webPane scrollToTop];
else
[webPane scrollToBottom];
}
}
}
/* handleReload
* Reload the current web page.
*/
-(IBAction)handleReload:(id)sender
{
if ([[webPane mainFrame] dataSource] != nil)
[webPane reload:self];
else
[self handleAddress:self];
}
/* handleStopLoading webview
* Stop loading the current web page.
*/
-(void)handleStopLoading:(id)sender
{
[webPane stopLoading:self];
}
/* handleRSSPage
* Open the RSS feed for the current page.
*/
-(IBAction)handleRSSPage:(id)sender
{
if (rssPageURL != nil)
{
Folder * currentFolder = [NSApp currentFolder];
int currentFolderId = [currentFolder itemId];
int parentFolderId = [currentFolder parentId];
if ([currentFolder firstChildId] > 0)
{
parentFolderId = currentFolderId;
currentFolderId = 0;
}
[[NSApp delegate] createNewSubscription:rssPageURL underFolder:parentFolderId afterChild:currentFolderId];
}
}
/* isLoading
* Returns whether the current web page is in the process of being loaded.
*/
-(BOOL)isLoading
{
return isLoading;
}
/* isProcessing
* Synonymous function that enables the progress indicator on the active tab.
*/
-(BOOL)isProcessing
{
return [self isLoading];
}
/* handleKeyDown [delegate]
* Support special key codes. If we handle the key, return YES otherwise
* return NO to allow the framework to pass it on for default processing.
*/
-(BOOL)handleKeyDown:(unichar)keyChar withFlags:(unsigned int)flags
{
return NO;
}
/* dealloc
* Clean up when the view is being deleted.
*/
-(void)dealloc
{
[viewTitle release];
[rssPageURL release];
[webPane setFrameLoadDelegate:nil];
[webPane setUIDelegate:nil];
[webPane stopLoading:self];
[webPane removeFromSuperviewWithoutNeedingDisplay];
[lastError release];
[pageFilename release];
[super dealloc];
}
@end
Jump to Line
Something went wrong with that request. Please try again.