Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mac OSX WebView - favicons, new windows, user agent #106

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/PluginAuto/Mac/PluginWindowMac.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ namespace FB {
// Handles a timer event
virtual void handleTimerEvent();

const char* getUserAgent() const;

protected:
Npapi::NpapiBrowserHostPtr m_npHost;
PluginEventMacWeakPtr m_PluginEvent;
Expand Down
5 changes: 5 additions & 0 deletions src/PluginAuto/Mac/PluginWindowMac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -322,3 +322,8 @@ static void timerCallback(NPP npp, uint32_t timerID) {
else
m_npHost->InvalidateRect(&r);
}

const char* PluginWindowMac::getUserAgent() const
{
return m_npHost->UserAgent();
}
3 changes: 3 additions & 0 deletions src/libs/WebView/Mac/WebViewMac.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ namespace FB { namespace View {
WebViewHelper* helper;
};
}}

#else
namespace FB { namespace View {
struct WebView_ObjCObjects;
Expand Down Expand Up @@ -162,7 +163,9 @@ namespace FB { namespace View {
}

virtual bool doWebViewNavigation(const std::string& url);
virtual bool doWebViewNewWindow(const std::string& url);
virtual bool doWebViewTitleChanged(const std::string& title);
virtual bool doWebViewFaviconChanged(const std::string b64_favicon);

private:
boost::scoped_ptr<WebView_ObjCObjects> o;
Expand Down
205 changes: 176 additions & 29 deletions src/libs/WebView/Mac/WebViewMac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,121 @@
#define OFFSCREEN_ORIGIN_X -4000
#define OFFSCREEN_ORIGIN_Y -4000

NSString* base64forData(NSData* theData);

NSString* base64forData(NSData* theData) {
const uint8_t* input = (const uint8_t*)[theData bytes];
NSInteger length = [theData length];

static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

NSMutableData* data = [NSMutableData dataWithLength:((length + 2) / 3) * 4];
uint8_t* output = (uint8_t*)data.mutableBytes;

NSInteger i;
for (i=0; i < length; i += 3) {
NSInteger value = 0;
NSInteger j;
for (j = i; j < (i + 3); j++) {
value <<= 8;

if (j < length) {
value |= (0xFF & input[j]);
}
}

NSInteger theIndex = (i / 3) * 4;
output[theIndex + 0] = table[(value >> 18) & 0x3F];
output[theIndex + 1] = table[(value >> 12) & 0x3F];
output[theIndex + 2] = (i + 1) < length ? table[(value >> 6) & 0x3F] : '=';
output[theIndex + 3] = (i + 2) < length ? table[(value >> 0) & 0x3F] : '=';
}

return [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease];
}

// WebViewExternalLinkHandler is a hack to get the request for a new window
// appearing via a pop-up. The problem is decidePolicyForNavigationAction
// does not do what you expect, so pretty much only gets called when the window
// first appears, so this is a thin shim to fool the WebView.

typedef void(^NewWindowCallback)(NSURL *url);
@interface WebViewExternalLinkHandler : NSObject
+(WebView *)riggedWebViewWithLoadHandler:(NewWindowCallback)handler;
@end

@interface WebViewExternalLinkHandler()
@property (strong, nonatomic) WebView *attachedWebView;
@property (strong, nonatomic) WebViewExternalLinkHandler *retainedSelf;
@property (copy, nonatomic) NewWindowCallback handler;
@end

@implementation WebViewExternalLinkHandler

-(id)init {
if (self = [super init]) {
// Create a new webview with self as the policyDelegate, and keep a ref to it
self.attachedWebView = [WebView new];
self.attachedWebView.policyDelegate = self;
}

return self;
}

-(void)webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
// Execute new pop-up handler
if (self.handler) {
self.handler([actionInformation objectForKey:WebActionOriginalURLKey]);
}

// Done, so safe to unretain yourself
self.retainedSelf = nil;
}

+(WebView *)riggedWebViewWithLoadHandler:(NewWindowCallback)handler {
WebViewExternalLinkHandler *newWindowHandler = [WebViewExternalLinkHandler new];
newWindowHandler.handler = handler;

// Retain yourself so that we persist until the
// webView:decidePolicyForNavigationAction:request:frame:decisionListener:
// method has been called
newWindowHandler.retainedSelf = newWindowHandler;

// Return the attached webview
return newWindowHandler.attachedWebView;
}

@end

@implementation WebViewHelper

-(void)webView:(WebView *)sender didReceiveIcon:(NSImage *)image forFrame:(WebFrame *)frame
{
NSBitmapImageRep *bits = [[NSBitmapImageRep alloc] initWithData:[image TIFFRepresentation]];
NSData *imageData;
imageData = [bits representationUsingType: NSPNGFileType
properties: nil];
NSString *imageString = base64forData(imageData);
controller->doWebViewFaviconChanged([imageString UTF8String]);
}

// decidePolicyForNewWindowAction does not work so we have to catch the creation of a new
// web view and send it to the right placer. Even if decidePolicyForNewWindowAction it still
// fails because the request is invariably empty.
-(WebView *)webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
return [WebViewExternalLinkHandler riggedWebViewWithLoadHandler:^(NSURL *url) {
controller->doWebViewNewWindow([[url absoluteString] UTF8String]);
}];
}

- (void)webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id < WebPolicyDecisionListener >)listener
{
int navType = [[actionInformation objectForKey:WebActionNavigationTypeKey] intValue];
// Let the plugin handle the event if it wants
if (controller &&
[frame isEqual:[sender mainFrame]]) {
[frame isEqual:[sender mainFrame]] &&
(navType != WebNavigationTypeBackForward) &&
(navType != WebNavigationTypeReload)) {

controller->doWebViewNavigation([[[request URL] absoluteString] UTF8String]);
[listener use];
Expand Down Expand Up @@ -71,7 +179,7 @@ - (id)initWithFrame:(NSRect)frameRect {
[webView setFrameLoadDelegate:self];
[webView setWantsLayer:YES];
[webView setPolicyDelegate:self];
[webView setCustomUserAgent: @"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/536.28.4 (KHTML, like Gecko) Version/6.0.3 Safari/536.28.4"];
[webView setUIDelegate:self];

[hiddenWindow setContentView:webView];
windowContext = [[NSGraphicsContext graphicsContextWithWindow:hiddenWindow] retain];
Expand Down Expand Up @@ -242,6 +350,11 @@ - (void)dealloc {
o->helper = [[WebViewHelper alloc] initWithFrame:frame];
[o->helper setController:this];

const char* user_agent(wnd->getUserAgent());
if (user_agent) {
[o->helper.webView setCustomUserAgent: [NSString stringWithUTF8String:user_agent]];
}

if (FB::PluginWindowMac::DrawingModelCoreGraphics == wnd->getDrawingModel()) {
// Core Graphics rendering set up.
m_isInvalidating = true;
Expand Down Expand Up @@ -346,7 +459,7 @@ - (void)dealloc {

// NSView* resp = [[o->helper.hiddenWindow contentView] hitTest:where];

NSEventType evtType;
NSEventType evtType = 0;

// std::stringstream ss;
// ss << "Mouse down at " << where.x << ", " << where.y;
Expand All @@ -359,23 +472,30 @@ - (void)dealloc {
case FB::MouseButtonEvent::MouseButton_Right:
evtType = NSRightMouseDown;
break;
case FB::MouseButtonEvent::MouseButton_Middle:
evtType = NSRightMouseDown;
break;
case FB::MouseButtonEvent::MouseButton_None:
default:
break;
}

// NSGraphicsContext *context = [NSGraphicsContext currentContext];
NSEvent *mouseDown = [NSEvent mouseEventWithType:evtType
location:where
modifierFlags:nil
timestamp:[NSDate timeIntervalSinceReferenceDate]
windowNumber:[o->helper.hiddenWindow windowNumber]
context:[o->helper context]
eventNumber:nil
clickCount:1
pressure:nil];

//NSLog(@"%@", o->helper.hiddenWindow.firstResponder);
[o->helper.hiddenWindow.firstResponder mouseDown:mouseDown];
if (evtType != 0) {
// NSGraphicsContext *context = [NSGraphicsContext currentContext];
NSEvent *mouseDown = [NSEvent mouseEventWithType:evtType
location:where
modifierFlags:nil
timestamp:[NSDate timeIntervalSinceReferenceDate]
windowNumber:[o->helper.hiddenWindow windowNumber]
context:[o->helper context]
eventNumber:nil
clickCount:1
pressure:nil];

// NSLog(@"%@", o->helper.hiddenWindow.firstResponder);
[o->helper.hiddenWindow.firstResponder mouseDown:mouseDown];
}

if (m_isInvalidating) {
wnd->InvalidateWindow();
}
Expand All @@ -388,7 +508,7 @@ - (void)dealloc {
where.y = wnd->getWindowHeight()-evt->m_y;

// NSView* resp = [[o->helper.hiddenWindow contentView] hitTest:where];
NSEventType evtType;
NSEventType evtType = 0;

// std::stringstream ss;
// ss << "Mouse up at " << where.x << ", " << where.y;
Expand All @@ -402,22 +522,29 @@ - (void)dealloc {
case FB::MouseButtonEvent::MouseButton_Right:
evtType = NSRightMouseUp;
break;
case FB::MouseButtonEvent::MouseButton_Middle:
evtType = NSRightMouseUp;
break;
case FB::MouseButtonEvent::MouseButton_None:
default:
break;
}

// NSGraphicsContext *context = [NSGraphicsContext currentContext];
NSEvent *mouseEvent = [NSEvent mouseEventWithType:evtType
location:where
modifierFlags:nil
timestamp:[NSDate timeIntervalSinceReferenceDate]
windowNumber:[o->helper.hiddenWindow windowNumber]
context:[o->helper context]
eventNumber:nil
clickCount:1
pressure:nil];
// NSLog(@"%@", o->helper.hiddenWindow.firstResponder);
[o->helper.hiddenWindow.firstResponder mouseUp:mouseEvent];
if (evtType != 0) {
// NSGraphicsContext *context = [NSGraphicsContext currentContext];
NSEvent *mouseEvent = [NSEvent mouseEventWithType:evtType
location:where
modifierFlags:nil
timestamp:[NSDate timeIntervalSinceReferenceDate]
windowNumber:[o->helper.hiddenWindow windowNumber]
context:[o->helper context]
eventNumber:nil
clickCount:1
pressure:nil];
// NSLog(@"%@", o->helper.hiddenWindow.firstResponder);
[o->helper.hiddenWindow.firstResponder mouseUp:mouseEvent];
}

if (m_isInvalidating) {
wnd->InvalidateWindow();
}
Expand Down Expand Up @@ -553,6 +680,16 @@ - (void)dealloc {
return true;
}

bool FB::View::WebViewMac::doWebViewNewWindow(const std::string& url)
{
FB::WebViewNewWindow navEv(url);
if (m_wnd) {
m_wnd->SendEvent(&navEv);
}

return true;
}

bool FB::View::WebViewMac::doWebViewTitleChanged(const std::string& title)
{
FB::WebViewTitleChanged titleEv(title);
Expand All @@ -562,3 +699,13 @@ - (void)dealloc {

return true;
}

bool FB::View::WebViewMac::doWebViewFaviconChanged(const std::string b64_favicon)
{
FB::WebViewFaviconChanged faviconEv(b64_favicon);
if (m_wnd) {
m_wnd->SendEvent(&faviconEv);
}

return true;
}
32 changes: 32 additions & 0 deletions src/libs/WebView/WebViewEvents.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,22 @@ namespace FB {
std::string m_url; // The url being navigated to.
};

////////////////////////////////////////////////////////////////////////////////////////////////////
/// @class WebViewNewWindow
///
/// @brief Fired when a new window event is received.
////////////////////////////////////////////////////////////////////////////////////////////////////
class WebViewNewWindow : public PluginEvent
{
public:
WebViewNewWindow(const std::string& url)
: m_url(url)
{ }

public:
std::string m_url; // The url being navigated to.
};

////////////////////////////////////////////////////////////////////////////////////////////////////
/// @class WebViewTitleChanged
///
Expand All @@ -51,6 +67,22 @@ namespace FB {
public:
std::string m_title; // The new title.
};

////////////////////////////////////////////////////////////////////////////////////////////////////
/// @class WebViewFaviconChanged
///
/// @brief Fired when the WebView favicon changes.
////////////////////////////////////////////////////////////////////////////////////////////////////
class WebViewFaviconChanged : public PluginEvent
{
public:
WebViewFaviconChanged(const std::string& b64_favicon)
: m_b64_favicon(b64_favicon)
{ }

public:
std::string m_b64_favicon; // The new favicon base64 encoded PNG.
};
};

#endif