diff --git a/src/PluginAuto/Mac/PluginWindowMac.h b/src/PluginAuto/Mac/PluginWindowMac.h index 23ccb37a..d6195026 100644 --- a/src/PluginAuto/Mac/PluginWindowMac.h +++ b/src/PluginAuto/Mac/PluginWindowMac.h @@ -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; diff --git a/src/PluginAuto/Mac/PluginWindowMac.mm b/src/PluginAuto/Mac/PluginWindowMac.mm index b1466e02..9d643337 100644 --- a/src/PluginAuto/Mac/PluginWindowMac.mm +++ b/src/PluginAuto/Mac/PluginWindowMac.mm @@ -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(); +} diff --git a/src/libs/WebView/Mac/WebViewMac.h b/src/libs/WebView/Mac/WebViewMac.h index 0ba477cc..6bf1fd76 100644 --- a/src/libs/WebView/Mac/WebViewMac.h +++ b/src/libs/WebView/Mac/WebViewMac.h @@ -102,6 +102,7 @@ namespace FB { namespace View { WebViewHelper* helper; }; }} + #else namespace FB { namespace View { struct WebView_ObjCObjects; @@ -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 o; diff --git a/src/libs/WebView/Mac/WebViewMac.mm b/src/libs/WebView/Mac/WebViewMac.mm index 9415801d..002ddeef 100644 --- a/src/libs/WebView/Mac/WebViewMac.mm +++ b/src/libs/WebView/Mac/WebViewMac.mm @@ -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)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]; @@ -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]; @@ -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; @@ -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; @@ -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(); } @@ -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; @@ -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(); } @@ -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); @@ -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; +} diff --git a/src/libs/WebView/WebViewEvents.h b/src/libs/WebView/WebViewEvents.h index 6751e589..9e77bdb6 100644 --- a/src/libs/WebView/WebViewEvents.h +++ b/src/libs/WebView/WebViewEvents.h @@ -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 /// @@ -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