From cbe2bb54d3485937300c20c0c72282b3700dcc7d Mon Sep 17 00:00:00 2001 From: Garry Bodsworth Date: Tue, 7 May 2013 14:05:04 +0100 Subject: [PATCH 1/3] Do new window pop-up handling. --- src/libs/WebView/Mac/WebViewMac.h | 1 + src/libs/WebView/Mac/WebViewMac.mm | 78 +++++++++++++++++++++++++++++- src/libs/WebView/WebViewEvents.h | 16 ++++++ 3 files changed, 94 insertions(+), 1 deletion(-) diff --git a/src/libs/WebView/Mac/WebViewMac.h b/src/libs/WebView/Mac/WebViewMac.h index 0ba477cc..2c312615 100644 --- a/src/libs/WebView/Mac/WebViewMac.h +++ b/src/libs/WebView/Mac/WebViewMac.h @@ -162,6 +162,7 @@ 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); private: diff --git a/src/libs/WebView/Mac/WebViewMac.mm b/src/libs/WebView/Mac/WebViewMac.mm index 9415801d..a35fc7e9 100644 --- a/src/libs/WebView/Mac/WebViewMac.mm +++ b/src/libs/WebView/Mac/WebViewMac.mm @@ -22,13 +22,78 @@ #define OFFSCREEN_ORIGIN_X -4000 #define OFFSCREEN_ORIGIN_Y -4000 +// 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 +// 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]; @@ -72,6 +137,7 @@ - (id)initWithFrame:(NSRect)frameRect { [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]; @@ -553,6 +619,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); diff --git a/src/libs/WebView/WebViewEvents.h b/src/libs/WebView/WebViewEvents.h index 6751e589..57c15973 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 /// From 7733bcdfa40a1162aa92607836b565acb16550ac Mon Sep 17 00:00:00 2001 From: Garry Bodsworth Date: Wed, 8 May 2013 11:51:28 +0100 Subject: [PATCH 2/3] Detect the favicon change and callback with base64 encoded png. --- src/libs/WebView/Mac/WebViewMac.h | 2 + src/libs/WebView/Mac/WebViewMac.mm | 121 ++++++++++++++++++++++------- src/libs/WebView/WebViewEvents.h | 16 ++++ 3 files changed, 112 insertions(+), 27 deletions(-) diff --git a/src/libs/WebView/Mac/WebViewMac.h b/src/libs/WebView/Mac/WebViewMac.h index 2c312615..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; @@ -164,6 +165,7 @@ 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 a35fc7e9..3fff855f 100644 --- a/src/libs/WebView/Mac/WebViewMac.mm +++ b/src/libs/WebView/Mac/WebViewMac.mm @@ -22,6 +22,39 @@ #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 @@ -77,6 +110,16 @@ +(WebView *)riggedWebViewWithLoadHandler:(NewWindowCallback)handler { @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. @@ -412,7 +455,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; @@ -425,23 +468,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(); } @@ -454,7 +504,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; @@ -468,22 +518,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(); } @@ -638,3 +695,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 57c15973..9e77bdb6 100644 --- a/src/libs/WebView/WebViewEvents.h +++ b/src/libs/WebView/WebViewEvents.h @@ -67,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 From 968932208653963fa4a4b0a86bc035e3c590d622 Mon Sep 17 00:00:00 2001 From: Garry Bodsworth Date: Fri, 10 May 2013 10:39:36 +0100 Subject: [PATCH 3/3] Use the user agent of the browser. --- src/PluginAuto/Mac/PluginWindowMac.h | 2 ++ src/PluginAuto/Mac/PluginWindowMac.mm | 5 +++++ src/libs/WebView/Mac/WebViewMac.mm | 6 +++++- 3 files changed, 12 insertions(+), 1 deletion(-) 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.mm b/src/libs/WebView/Mac/WebViewMac.mm index 3fff855f..002ddeef 100644 --- a/src/libs/WebView/Mac/WebViewMac.mm +++ b/src/libs/WebView/Mac/WebViewMac.mm @@ -179,7 +179,6 @@ - (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]; @@ -351,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;