From bb8c0f37dea42b72a479aee591b098f6a2a594a1 Mon Sep 17 00:00:00 2001 From: Joey Korkames Date: Tue, 24 Feb 2015 16:34:36 -0700 Subject: [PATCH] added tab-level scriptability, fleshed out error popup screens --- modules/MacPin/AppDelegate.swift | 39 ++++---- modules/MacPin/AppScriptRuntime.swift | 13 +-- modules/MacPin/BrowserViewController.swift | 80 +++++------------ modules/MacPin/URLBoxController.swift | 18 +++- modules/MacPin/WebViewController.swift | 100 +++++++++------------ modules/MacPin/WebViewDelegates.swift | 55 +++++------- sites/Hangouts/app.js | 20 ++--- 7 files changed, 142 insertions(+), 183 deletions(-) diff --git a/modules/MacPin/AppDelegate.swift b/modules/MacPin/AppDelegate.swift index 6b17d7c..8350e7f 100644 --- a/modules/MacPin/AppDelegate.swift +++ b/modules/MacPin/AppDelegate.swift @@ -31,17 +31,14 @@ public class AppDelegate: NSObject, NSApplicationDelegate { super.init() } - func conlog(msg: String) { - browserController.conlog(msg) - //warn(msg) - } + func conlog(msg: String) { browserController.conlog(msg) } - //public func applicationDockMenu(_ sender: NSApplication) -> NSMenu? + //public func applicationDockMenu(sender: NSApplication) -> NSMenu? public func applicationShouldOpenUntitledFile(app: NSApplication) -> Bool { return false } //public func application(sender: AnyObject, openFileWithoutUI filename: String) -> Bool {} // app terminates on return! - //public func application(_ theApplication: NSApplication, printFile filename: String) -> Bool () //app terminates on return + //public func application(theApplication: NSApplication, printFile filename: String) -> Bool () //app terminates on return public func applicationWillFinishLaunching(notification: NSNotification) { // dock icon bounces, also before self.openFile(foo.bar) is called NSUserNotificationCenter.defaultUserNotificationCenter().delegate = self @@ -50,12 +47,7 @@ public class AppDelegate: NSObject, NSApplicationDelegate { // menu item actions walk the responder chain // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/EventOverview/EventArchitecture/EventArchitecture.html#//apple_ref/doc/uid/10000060i-CH3-SW9 // https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKit/#10_10ViewController - // ChildView -> ParentView -> ParentView's ViewController -> ParentView's ParentView -> Window -> Window's WindowController -> Window's Delegate -> NSApp -> App Delegate - // where do ChildViewControllers fit in here? - - //urlbox doesn't forward up to any viewcontrollers when it is first responder, seems to just sit directly under NSWindow in the chain as it is a NSToolBarItem's child - - // make browserController forward all selectors to active childviewcontrollers http://stackoverflow.com/a/11468081/3878712 + // ChildView -> ChildView ViewController -> ParentView -> ParentView's ViewController -> ParentView's ParentView -> Window -> Window's WindowController -> Window's Delegate -> NSApp -> App Delegate app!.mainMenu = NSMenu() @@ -118,6 +110,7 @@ public class AppDelegate: NSObject, NSApplicationDelegate { dbgMenu.submenu?.title = "Debug" dbgMenu.submenu?.easyAddItem("Highlight TabView Constraints", "highlightConstraints") dbgMenu.submenu?.easyAddItem("Randomize TabView Layout", "exerciseAmbiguityInLayout") + dbgMenu.submenu?.easyAddItem("Replace contentView With Tab", "replaceContentView") #if DBGMENU #else dbgMenu.hidden = true @@ -142,13 +135,13 @@ public class AppDelegate: NSObject, NSApplicationDelegate { jsruntime.loadSiteApp() // load app.js, if present if let default_html = NSBundle.mainBundle().URLForResource("default", withExtension: "html") { - conlog("loading initial page from app bundle: \(default_html)") + conlog("\(__FUNCTION__) loading initial page from app bundle: \(default_html)") browserController.newTab(default_html.description) } if countElements(Process.arguments) > 1 { // got argv var urlstr = Process.arguments[1] ?? "about:blank" - conlog("loading initial page from argv[1]: \(urlstr)") + conlog("\(__FUNCTION__) loading initial page from argv[1]: \(urlstr)") browserController.newTab(urlstr) browserController.unhideApp() } @@ -168,8 +161,22 @@ public class AppDelegate: NSObject, NSApplicationDelegate { //if application?.orderedDocuments?.count < 1 { showApplication(self) } } - public func application(application: NSApplication, willPresentError error: NSError) -> NSError { return error } //customize error messages - // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ErrorHandlingCocoa/HandleReceivedError/HandleReceivedError.html + public func application(application: NSApplication, willPresentError error: NSError) -> NSError { + conlog("\(__FUNCTION__) `\(error.localizedDescription)` [\(error.domain)] [\(error.code)] `\(error.localizedFailureReason ?? String())` : \(error.userInfo)") + if error.domain == NSURLErrorDomain { + if let userInfo = error.userInfo { + if let errstr = userInfo[NSLocalizedDescriptionKey] as? String { + if let url = userInfo[NSURLErrorFailingURLStringErrorKey] as? String { + var newUserInfo = userInfo + newUserInfo[NSLocalizedDescriptionKey] = "\(errstr)\n\n\(url)" // add failed url to error message + let newerror = NSError(domain: error.domain, code: error.code, userInfo: newUserInfo) + return newerror + } + } + } + } + return error + } public func applicationWillTerminate(notification: NSNotification) { NSUserDefaults.standardUserDefaults().synchronize() } diff --git a/modules/MacPin/AppScriptRuntime.swift b/modules/MacPin/AppScriptRuntime.swift index 5c117e9..6cf6202 100644 --- a/modules/MacPin/AppScriptRuntime.swift +++ b/modules/MacPin/AppScriptRuntime.swift @@ -20,6 +20,7 @@ public extension JSValue { @objc protocol OSXScriptExports : JSExport { // '$osx' in app.js var resourcePath: String { get } + //func log(msg: String) func registerURLScheme(scheme: String) func postNotification(title: String, _ subtitle: String?, _ msg: String) func openURL(urlstr: String, _ app: String?) @@ -27,10 +28,8 @@ public extension JSValue { public class AppScriptRuntime: NSObject, OSXScriptExports { - //var viewController: TabViewController - var context = JSContext(virtualMachine: JSVirtualMachine()) - var delegate: JSValue //= JSContext().evaluateScript("{};")! //default property-less delegate obj + var delegate: JSValue var resourcePath: String { get { return ( NSBundle.mainBundle().resourcePath ?? String() ) } } @@ -39,6 +38,8 @@ public class AppScriptRuntime: NSObject, OSXScriptExports { super.init() } + //func log(msg: String) { } + func loadSiteApp() { if let app_js = NSBundle.mainBundle().URLForResource("app", withExtension: "js") { // https://bugs.webkit.org/show_bug.cgi?id=122763 -- allow evaluteScript to specify source URL @@ -51,8 +52,10 @@ public class AppScriptRuntime: NSObject, OSXScriptExports { context.exception = exception //default in JSContext.mm return // gets returned to evaluateScript()? } - warn("loading setup script from app bundle: \(app_js)") + warn("\(__FUNCTION__): \(app_js)") context.setObject(self, forKeyedSubscript: "$osx") + //context.setObject(log, forKeyedSubscript: "log") + // ^ pass in block that will also copy to $browser.conlog() if let jsobj = loadJSCoreScriptFromBundle("app") { delegate = jsobj // FIXME JSValue is very ambiguous, should confirm it it is a {} @@ -85,7 +88,7 @@ public class AppScriptRuntime: NSObject, OSXScriptExports { //replyEvent == ?? } - // func newAppTrayItem + // func newTrayTab // support opening a page as a NSStatusItem icon by the clock // https://github.com/phranck/CCNStatusItem diff --git a/modules/MacPin/BrowserViewController.swift b/modules/MacPin/BrowserViewController.swift index a61c5ad..b0e2cbb 100644 --- a/modules/MacPin/BrowserViewController.swift +++ b/modules/MacPin/BrowserViewController.swift @@ -8,16 +8,12 @@ import JavaScriptCore // https://github.com/WebKit/webkit/tree/master/Source/Ja var isFullscreen: Bool { get set } var isToolbarShown: Bool { get set } var tabCount: Int { get } - var tabSelected: AnyObject? { get set } - func closeCurrentTab() + var tabSelected: WebViewController? { get set } func switchToNextTab() func switchToPreviousTab() func conlog(msg: String) // FIXME: must kill - func firstTabEvalJS(js: String) // FIXME: must kill - func currentTabEvalJS(js: String) // FIXME: must kill func newTabPrompt() - func newTab(params: [String:AnyObject]) -> String - //func newTab(params: JSValue) -> String + func newTab(params: [String:AnyObject]) -> WebViewController? func stealAppFocus() func unhideApp() } @@ -77,7 +73,8 @@ public class TabViewController: NSTabViewController { // & NSViewController override public func toolbarDefaultItemIdentifiers(toolbar: NSToolbar) -> [AnyObject] { let tabs = super.toolbarDefaultItemIdentifiers(toolbar) - return [ShareButton, NewTabButton] + tabs! + [NSToolbarFlexibleSpaceItemIdentifier] + //return [ShareButton, NewTabButton] + tabs! + [NSToolbarFlexibleSpaceItemIdentifier] + return [ShareButton, NewTabButton, NSToolbarFlexibleSpaceItemIdentifier] + tabs! //tabviewcontroller somehow remembers where tabs! was and keeps putting new tabs in that position } @@ -129,7 +126,9 @@ public class TabViewController: NSTabViewController { // & NSViewController default: // let NSTabViewController map a TabViewItem to this ToolbarItem - return super.toolbar(toolbar, itemForItemIdentifier: itemIdentifier, willBeInsertedIntoToolbar: flag) + let tvi = super.toolbar(toolbar, itemForItemIdentifier: itemIdentifier, willBeInsertedIntoToolbar: flag) + //warn("\(__FUNCTION__) \(itemIdentifier) \(tvi?.image) \(tvi?.action)") + return tvi } // optional func toolbarWillAddItem(_ notification: NSNotification) // optional func toolbarDidRemoveItem(_ notification: NSNotification) @@ -158,8 +157,6 @@ public class TabViewController: NSTabViewController { // & NSViewController view.layer?.masksToBounds = true // include layer contents in clipping effects view.canDrawSubviewsIntoLayer = true // coalesce all subviews' layers into this one - //view.autoresizesSubviews = false - //view.translatesAutoresizingMaskIntoConstraints = false view.autoresizingMask = .ViewWidthSizable | .ViewHeightSizable //resize tabview to match parent ContentView size tabStyle = .Toolbar // this will reinit window.toolbar to mirror the tabview itembar transitionOptions = .None //.Crossfade .SlideUp/Down/Left/Right/Forward/Backward @@ -180,9 +177,8 @@ public class TabViewController: NSTabViewController { // & NSViewController override public func viewWillAppear() { super.viewWillAppear() if let window = view.window { - //view.layer?.frame = window.contentLayoutRect - //window.toolbar!.allowsUserCustomization = false - //window.showsToolbarButton = false + window.toolbar!.allowsUserCustomization = true + window.toolbar!.displayMode = .IconOnly } } @@ -191,15 +187,6 @@ public class TabViewController: NSTabViewController { // & NSViewController } public class BrowserViewController: TabViewController, BrowserScriptExports { - //var tabs: [String] = [] - // didSet { for id, wvc in added { jsruntime.context.getObject(forKey: "$tabs").setValue(webVC, forKey: id) } } - /* - as tabs are made and destroyed, the WvCs.identifiers are put in here - then WebViewControllers can export their funcs so the script runtime can directly manipulate open tabs - using $browser.tabs[id].print() and whatnot - */ - //var tabs: [String: WebViewController] = [:] - // didSet { for id, wvc in added { jsruntime.setValue(tabs, forKey: "$tabs") } } var _globalUserScripts: [String] = [] var globalUserScripts: [String] { // protect the app from bad JS settings? @@ -253,39 +240,18 @@ public class BrowserViewController: TabViewController, BrowserScriptExports { set(bool) { if bool != isToolbarShown { view.window!.toggleToolbarShown(nil) } } } - //var tabCount: Int { get { return tabViewItems.count } } - var firstTab: NSTabViewItem? { get { return (tabViewItems.first as? NSTabViewItem) ?? nil }} - var currentTab: NSTabViewItem? { get { - if selectedTabViewItemIndex == -1 { return nil } - return (tabViewItems[selectedTabViewItemIndex] as? NSTabViewItem) ?? nil - }} - var tabCount: Int { get { return childViewControllers.count } } - var firstWebVC: WebViewController? { get { return (childViewControllers.first as? WebViewController) ?? nil }} - var currentWebVC: WebViewController? { get { - if selectedTabViewItemIndex == -1 { return nil } - return (childViewControllers[selectedTabViewItemIndex] as? WebViewController) ?? nil - }} - var currentWebVC2: WebViewController? { get { - return ( presentedViewControllers?.first as? WebViewController ) - }} - - func firstTabEvalJS(js: String) { firstWebVC?.webview?.evaluateJavaScript(js, completionHandler: nil) } - func currentTabEvalJS(js: String) { currentWebVC?.webview?.evaluateJavaScript(js, completionHandler: nil) } - - /* var tabSelected: Int { // the number goes stale when tabs are removed and rearranged - get { return viewController.selectedTabViewItemIndex } - set(idx) { viewController.tabView.selectTabViewItemAtIndex(idx) } - }*/ - - var tabSelected: AnyObject? { - get { return tabView.selectedTabViewItem?.identifier } - set(id) { tabView.selectTabViewItemWithIdentifier(id!) } + var firstTab: WebViewController? { get { return (childViewControllers.first as? WebViewController) ?? nil }} + var tabSelected: WebViewController? { + get { + if selectedTabViewItemIndex == -1 { return nil } + return (childViewControllers[selectedTabViewItemIndex] as? WebViewController) ?? nil + } + set(wvc) { tabView.selectTabViewItem(tabViewItemForViewController(wvc!)) } } func closeCurrentTab() { - //if let tab = currentTab { removeTabViewItem(tab) } - currentWebVC?.removeFromParentViewController() + tabSelected?.removeFromParentViewController() //FIXME: not dealloc'ing the actual webviews if tabView.numberOfTabViewItems == 0 { view.window?.performClose(self) } else { switchToNextTab() } //safari behavior } @@ -299,7 +265,8 @@ public class BrowserViewController: TabViewController, BrowserScriptExports { func newTabPrompt() { if let wvc = newTab(NSURL(string: "about:blank")!) { - if let id: AnyObject = tabViewItemForViewController(wvc)?.identifier { tabSelected = id } + //if let id: AnyObject = tabViewItemForViewController(wvc)?.identifier { tabSelected = id } + tabSelected = wvc wvc.gotoButtonClicked(nil) } } @@ -307,7 +274,7 @@ public class BrowserViewController: TabViewController, BrowserScriptExports { // exported to jsruntime.context as $browser.newTab({url: 'http://example.com'}) // NEEDS TO BE DECLARED FIRST (above all other newTab overload funcs) in order for jsruntime to use this. crazy, amirite? // FIXME: (params: JSValue)->JSValue would be safer? and prevent this selector confusion? - func newTab(params: [String:AnyObject]) -> String { + func newTab(params: [String:AnyObject]) -> WebViewController? { // prototype vars var url = NSURL(string: "about:blank") var icon: String? = nil @@ -346,10 +313,10 @@ public class BrowserViewController: TabViewController, BrowserScriptExports { for handler in msgHandlers { webctl.addScriptMessageHandler(wvc, name: handler) } newTab(wvc, withIcon: icon) wvc.gotoURL(url) - return wvc.identifier + return wvc } } - return "\(__FUNCTION__) failed: url invalid!" + return nil } func newTab(urlstr: String) { if let url = NSURL(string: urlstr) { newTab(url) } } func newTab(url: NSURL) -> WebViewController? { @@ -372,6 +339,7 @@ public class BrowserViewController: TabViewController, BrowserScriptExports { // https://developer.apple.com/library/ios/documentation/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html // just C, no Cocoa API yet: WKIconDatabaseCopyIconURLForPageURL(icodb, url) WKIconDatabaseCopyIconDataForPageURL(icodb, url) // also https://github.com/WebKit/webkit/commit/660d1e55c2d1ee19ece4a7565d8df8cab2e15125 + // OSX has libxml2 built-in https://developer.apple.com/library/mac/samplecode/LinkedImageFetcher/Listings/Read_Me_About_LinkedImageFetcher_txt.html } tab.initialFirstResponder = subvc.view addTabViewItem(tab) @@ -380,7 +348,7 @@ public class BrowserViewController: TabViewController, BrowserScriptExports { func conlog(msg: String) { // _ webview: WKWebView? = nil warn(msg) #if APP2JSLOG - currentTabEvalJS("console.log(\'<\(__FILE__)> \(msg)\')") + tabSelected?.evalJS("console.log(\'<\(__FILE__)> \(msg)\')") #endif } diff --git a/modules/MacPin/URLBoxController.swift b/modules/MacPin/URLBoxController.swift index b296868..96aca74 100644 --- a/modules/MacPin/URLBoxController.swift +++ b/modules/MacPin/URLBoxController.swift @@ -1,3 +1,10 @@ +import WebKit +public extension WKWebView { + override func setValue(value: AnyObject?, forUndefinedKey key: String) { + if key != "URL" { super.setValue(value, forUndefinedKey: key) } //prevents URLbox from trying to directly rewrite webView.URL + } +} + import Cocoa class URLBoxController: NSViewController { @@ -19,7 +26,7 @@ class URLBoxController: NSViewController { } } - init?(urlbox: NSTextField) { + init?(urlbox: NSTextField) { //prepareForReuse()? super.init(nibName: nil, bundle:nil) urlbox.bezelStyle = .RoundedBezel urlbox.drawsBackground = false @@ -46,6 +53,7 @@ class URLBoxController: NSViewController { // http://stackoverflow.com/questions/3605438/validate-decimal-max-min-values-in-a-nstextfield?rq=1 } view = urlbox + //view.addSubview(progbar) preferredContentSize = CGSize(width: 600, height: 24) } @@ -53,6 +61,12 @@ class URLBoxController: NSViewController { super.viewDidLoad() } + override func viewWillAppear() { + super.viewWillAppear() + //progbar.removeFromSuperview() + //urlbox.sizeToFit() + } + override func viewDidAppear() { super.viewDidAppear() //progbar.style = .BarStyle // .SpinningStyle @@ -68,6 +82,8 @@ class URLBoxController: NSViewController { if let wvc = representedObject? as? WebViewController { view.window?.makeFirstResponder(wvc.view) wvc.gotoURL(url) + //view.addSubview(progbar) + //wait for isLoaded == true presentingViewController?.dismissViewController(self) //if a thoust art a popover, close thyself } } else { diff --git a/modules/MacPin/WebViewController.swift b/modules/MacPin/WebViewController.swift index 55fd050..43c570d 100644 --- a/modules/MacPin/WebViewController.swift +++ b/modules/MacPin/WebViewController.swift @@ -2,7 +2,6 @@ func close() func evalJS(js: String) func gotoURL(urlstr: String) - func stealFocus() } class WebViewController: NSViewController { @@ -47,7 +46,6 @@ class WebViewController: NSViewController { // enables Safari.app->Develop-> remote debugging of JSCore and all webviews' JS threads // http://stackoverflow.com/questions/11361822/debug-ios-67-mobile-safari-using-the-chrome-devtools #endif - //webview._editable = true // https://github.com/WebKit/webkit/tree/master/Tools/WebEditingTester // https://github.com/WebKit/webkit/blob/master/Source/WebCore/page/mac/UserAgentMac.mm#L48 @@ -57,15 +55,11 @@ class WebViewController: NSViewController { webview.navigationDelegate = self // allows/denies navigation actions webview.UIDelegate = self //alert(), window.open() - let wkview = (webview.subviews.first as WKView) // 1 per frame? - //wkview.shouldExpandToViewHeightForAutoLayout = false - //wkview.setValue(false, forKey: "shouldExpandToViewHeightForAutoLayout") //KVO - webview.identifier = "WKWebView:" + NSUUID().UUIDString //set before installing into a view/window, then get ... else crash! view = webview identifier = "WebViewController<\(webview.identifier)>" - bind(NSTitleBinding, toObject: view, withKeyPath: "title", options: nil) - bind("representedObject", toObject: view, withKeyPath: "URL", options: nil) + //bind(NSTitleBinding, toObject: view, withKeyPath: "title", options: nil) + //bind("representedObject", toObject: view, withKeyPath: "URL", options: nil) } override func loadView() { @@ -74,36 +68,22 @@ class WebViewController: NSViewController { override func viewDidLoad() { super.viewDidLoad() - //view.frame = NSMakeRect(0, 0, 600, 800) // default size - //view.frame = parentViewController.view.bounds - //view.frame = parentViewController.view.window!.contentLayoutRect - //view.frame = CGRect(x: 0, y: 0, - // width: CGRectGetWidth(parentViewController.view.bounds), - // height: CGRectGetHeight(parentViewController.view.bounds)) - //view.frame = view.window!.windowController.window!.contentView.frame - //view.frame = parentViewController.view.frame - // http://ashfurrow.com/blog/you-probably-dont-understand-frames-and-bounds/ - // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaViewsGuide/WorkingWithAViewHierarchy/WorkingWithAViewHierarchy.html#//apple_ref/doc/uid/TP40002978-CH4-SW12 - - //view.wantsLayer = true //use CALayer to coalesce views - //view.layer?.frame = parentViewController!.view.frame - //view.layer?.frame = view.window!.contentLayoutRect - view.autoresizingMask = .ViewWidthSizable | .ViewHeightSizable - view.postsFrameChangedNotifications = false //FIXME stop webinspector flicker? + if let wkview = view.subviews.first as? WKView { + //wkview.setValue(false, forKey: "shouldExpandToViewHeightForAutoLayout") //KVO + //wkview.shouldExpandToViewHeightForAutoLayout = false + //wkview.postsFrameChangedNotifications = false //FIXME stop webinspector flicker? + //view.postsBoundsChangedNotifications = false //FIXME stop webinspector flicker? + } } override func viewWillAppear() { // window popping up with this tab already selected & showing super.viewWillAppear() - if let window = view.window { - //window.makeFirstResponder(view) - //warn("\(__FUNCTION__) \(window.firstResponder)") - } if let webview = webview { if let browser = browser { webview._drawsTransparentBackground = browser.isTransparent } } // use KVC to auto frob this? } - override func viewDidAppear() { // window popping up. & tab switch? + override func viewDidAppear() { super.viewDidAppear() //view.autoresizesSubviews = false //view.translatesAutoresizingMaskIntoConstraints = false @@ -112,14 +92,21 @@ class WebViewController: NSViewController { override func viewWillLayout() { #if WK2LOG //warn("\(__FUNCTION__) WKWebView \(view.frame)") - warn("\(__FUNCTION__) WKWebView subviews #\(view.subviews.count)") - warn("\(__FUNCTION__) WKWebView autoResizes subviews #\(view.autoresizesSubviews)") + //warn("\(__FUNCTION__) WKWebView subviews #\(view.subviews.count)") + //warn("\(__FUNCTION__) WKWebView autoResizes subviews \(view.autoresizesSubviews)")i + + // https://github.com/WebKit/webkit/blob/aefe74816c3a4db1b389893469e2d9475bbc0fd9/Source/WebKit2/UIProcess/mac/WebInspectorProxyMac.mm#L809 + // inspectedViewFrameDidChange is looping itself + // ignoreNextInspectedViewFrameDidChange is supposed to prevent this + if let wkview = view.subviews.first? as? NSView { - if wkview.autoresizingMask == .ViewWidthSizable | .ViewMaxYMargin { - // https://github.com/WebKit/webkit/blob/aefe74816c3a4db1b389893469e2d9475bbc0fd9/Source/WebKit2/UIProcess/mac/WebInspectorProxyMac.mm#L809 - warn("\(__FUNCTION__) got bottom-anchored WKView \(wkview.frame)") - if let pageView = view.subviews.last? as? NSView { - pageView.autoresizingMask = .ViewWidthSizable + if wkview.tag == 1000 { //WKInspectorViewTag + // wkview.autoresizingMask == .ViewWidthSizable | .ViewMaxYMargin { + warn("\(__FUNCTION__) webinpector layout \(wkview.frame)") //probably a webinspector + if let pageView = view.subviews.last? as? NSView { // tag=-1 should be the page content + warn("\(__FUNCTION__) page layout \(pageView.frame)") + //pageView.autoresizingMask = .ViewWidthSizable + //pageView.autoresizingMask = .ViewNotSizable } } } @@ -136,11 +123,10 @@ class WebViewController: NSViewController { //view.removeConstraints(view.constraints) } - //viewWillTransitionToSize(newSize: NSSize) { } - } extension WebViewController: WebViewScriptExports { // AppGUI / ScriptRuntime funcs + override func cancelOperation(sender: AnyObject?) { webview?.stopLoading(sender) } //make Esc key stop page load func evalJS(js: String) { webview?.evaluateJavaScript(js, completionHandler: nil) } @@ -152,13 +138,9 @@ extension WebViewController: WebViewScriptExports { // AppGUI / ScriptRuntime fu } func close() { removeFromParentViewController() } - func stealFocus() { - //presentingViewController.tabView.selectTabViewItem(presentingViewController.tabViewItemForViewController(self)) - browser?.tabView.selectTabViewItem(browser?.tabViewItemForViewController(self)) - } func grabWindow() -> NSWindow? { - stealFocus() + browser?.tabSelected = self if let window = view.window { return window } return nil } @@ -170,6 +152,8 @@ extension WebViewController: WebViewScriptExports { // AppGUI / ScriptRuntime fu func highlightConstraints() { view.window?.visualizeConstraints(view.constraints) } + func replaceContentView() { view.window?.contentView = view } + func shareButtonClicked(sender: AnyObject?) { if let btn = sender? as? NSView { if let url = webview?.URL { @@ -206,25 +190,25 @@ extension WebViewController: WebViewScriptExports { // AppGUI / ScriptRuntime fu } func askToOpenURL(url: NSURL) { - if let opener = NSWorkspace.sharedWorkspace().URLForApplicationToOpenURL(url) { - conlog("prompting to externally open URL: [\(url)]") - let alert = NSAlert() - alert.messageText = "Open using App:\n\(opener)" - alert.addButtonWithTitle("OK") - alert.addButtonWithTitle("Cancel") - alert.informativeText = url.description - alert.icon = NSWorkspace.sharedWorkspace().iconForFile(opener.path!) - if let window = grabWindow() { + if let window = grabWindow() { + if let opener = NSWorkspace.sharedWorkspace().URLForApplicationToOpenURL(url) { + conlog("prompting to externally open URL: [\(url)]") + let alert = NSAlert() + alert.messageText = "Open using App:\n\(opener)" + alert.addButtonWithTitle("OK") + alert.addButtonWithTitle("Cancel") + alert.informativeText = url.description + alert.icon = NSWorkspace.sharedWorkspace().iconForFile(opener.path!) alert.beginSheetModalForWindow(window, completionHandler:{(response:NSModalResponse) -> Void in if response == NSAlertFirstButtonReturn { NSWorkspace.sharedWorkspace().openURL(url) } }) + return } - return - } - //FIXME: complain here - if let window = view.window { - //var error = NSError(domain: "ProcessName", code: 2, userInfo: [:]) - //view.presentError(error, modalForWindow: window, delegate: nil, didPresentSelector: nil, contextInfo: nil) } + let error = NSError(domain: "MacPin", code: 2, userInfo: [ + NSURLErrorKey: url, + NSLocalizedDescriptionKey: "No program present on this system is registered to open this non-browser URL.\n\n\(url)" + ]) + view.presentError(error, modalForWindow: window, delegate: nil, didPresentSelector: nil, contextInfo: nil) } } diff --git a/modules/MacPin/WebViewDelegates.swift b/modules/MacPin/WebViewDelegates.swift index c2cbd9c..4908cc6 100644 --- a/modules/MacPin/WebViewDelegates.swift +++ b/modules/MacPin/WebViewDelegates.swift @@ -5,21 +5,6 @@ import WebKit -public extension WKWebView { - func conlog(msg: String) { - warn(msg) -#if APP2JSLOG - evaluateJavaScript("console.log(\'<\(__FILE__)> \(msg)\')", completionHandler: nil) -#endif - } - - override func cancelOperation(sender: AnyObject?) { stopLoading(sender) } //make Esc key stop page load - - override func setValue(value: AnyObject?, forUndefinedKey key: String) { - if key != "URL" { super.setValue(value, forUndefinedKey: key) } - } -} - extension WebViewController: WKScriptMessageHandler { func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) { //called from JS: webkit.messageHandlers..postMessage(); @@ -44,7 +29,7 @@ extension WebViewController: WKScriptMessageHandler { // alert.icons should be the current tab icon, which may not be the app icon extension WebViewController: WKUIDelegate { func webView(webView: WKWebView!, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: () -> Void) { - webView.conlog("JS window.alert() -> \(message)") + conlog("JS window.alert() -> \(message)") var alert = NSAlert() alert.messageText = webView.title alert.addButtonWithTitle("Dismiss") @@ -59,7 +44,7 @@ extension WebViewController: WKUIDelegate { } func webView(webView: WKWebView!, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: (Bool) -> Void) { - webView.conlog("JS window.confirm() -> \(message)") + conlog("JS window.confirm() -> \(message)") var alert = NSAlert() alert.messageText = webView.title alert.addButtonWithTitle("OK") @@ -74,7 +59,7 @@ extension WebViewController: WKUIDelegate { } func webView(webView: WKWebView!, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: (String!) -> Void) { - webView.conlog("JS window.prompt() -> \(prompt)") + conlog("JS window.prompt() -> \(prompt)") var alert = NSAlert() alert.messageText = webView.title alert.addButtonWithTitle("Submit") @@ -97,7 +82,7 @@ extension WebViewController: WKUIDelegate { // http://www.cocoawithlove.com/2009/09/whereismymac-snow-leopard-corelocation.html func _webView(webView: WKWebView!, shouldRequestGeolocationAuthorizationForURL url: NSURL, isMainFrame: Bool, mainFrameURL: NSURL) -> Bool { return false } // always allow func _webView(webView: WKWebView!, printFrame: WKFrameInfo) { - webView.conlog("JS: window.print()") + conlog("JS: window.print()") var printer = NSPrintOperation(view: webView, printInfo: NSPrintInfo.sharedPrintInfo()) // seems to work very early on in the first loaded page, then just becomes blank pages // PDF = This view requires setWantsLayer:YES when blendingMode == NSVisualEffectBlendingModeWithinWindow @@ -117,7 +102,7 @@ extension WebViewController: WKUIDelegate { extension WebViewController: WKNavigationDelegate { func webView(webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { if let url = webView.URL { - webView.conlog("\(__FUNCTION__) [\(url)]") + conlog("\(__FUNCTION__) [\(url)]") //browser.navigations[navigation] = url //make a backref for when we may have to present an error later } } @@ -139,7 +124,7 @@ extension WebViewController: WKNavigationDelegate { if jsruntime.delegate.tryFunc("decideNavigationForURL", url.description) { decisionHandler(.Cancel); return } - //webView.conlog("WebView decidePolicy() navType:\(navigationAction.navigationType.rawValue) sourceFrame:(\(navigationAction.sourceFrame?.request.URL)) -> \(url)") + //conlog("WebView decidePolicy() navType:\(navigationAction.navigationType.rawValue) sourceFrame:(\(navigationAction.sourceFrame?.request.URL)) -> \(url)") switch navigationAction.navigationType { case .LinkActivated: let mousebtn = navigationAction.buttonNumber @@ -150,7 +135,7 @@ extension WebViewController: WKNavigationDelegate { if navigationAction.targetFrame != nil && mousebtn == 1 { fallthrough } // left-click on in_frame target link browser?.newTab(url) // middle-clicked, or out of frame target link } - webView.conlog("\(__FUNCTION__) -> .Cancel -- user clicked or middle-clicked: opening externally") + conlog("\(__FUNCTION__) -> .Cancel -- user clicked or middle-clicked: opening externally") decisionHandler(.Cancel) case .FormSubmitted: fallthrough case .BackForward: fallthrough @@ -174,7 +159,7 @@ extension WebViewController: WKNavigationDelegate { return case NSURLAuthenticationMethodDefault: fallthrough; default: - webView.conlog("prompting user for \(authMethod) credentials: [\(webView.URL ?? String())]") + conlog("prompting user for \(authMethod) credentials: [\(webView.URL ?? String())]") } } @@ -224,10 +209,10 @@ extension WebViewController: WKNavigationDelegate { if let url = webView.URL { //?.absoluteString ?? "about:blank" { // URL is the last requested (And invalid) domain name, just the last sucessfully-loaded location // need to pull the URL back from didStartProvisionalNavigation - webView.conlog("\(__FUNCTION__) [\(url)] -> `\(error.localizedDescription)` [\(error.domain)] [\(error.code)] `\(error.localizedFailureReason ?? String())`") + conlog("\(__FUNCTION__) [\(url)] -> `\(error.localizedDescription)` [\(error.domain)] [\(error.code)] `\(error.localizedFailureReason ?? String())` : \(error.userInfo)") if error.domain == NSURLErrorDomain && error.code != -999 && !webView.loading { // 999 is thrown often on working links if let window = grabWindow() { - webView.presentError(error, modalForWindow: window, delegate: nil, didPresentSelector: nil, contextInfo: nil) } + presentError(error, modalForWindow: window, delegate: nil, didPresentSelector: nil, contextInfo: nil) } } } } @@ -244,7 +229,7 @@ extension WebViewController: WKNavigationDelegate { if navigationResponse.canShowMIMEType { decisionHandler(.Allow) } else { - webView.conlog("webView cannot render requested MIME-type:\(mime) @ \(url)") + conlog("webView cannot render requested MIME-type:\(mime) @ \(url)") if !jsruntime.delegate.tryFunc("handleUnrenderableMIME", mime, url.description, fn) { askToOpenURL(url) } decisionHandler(.Cancel) } @@ -253,11 +238,11 @@ extension WebViewController: WKNavigationDelegate { func webView(webView: WKWebView, didFailNavigation navigation: WKNavigation!, withError error: NSError) { //error during commited main frame navigation // like server-issued error Statuses with no page content if let url = webView.URL { - webView.conlog("\(__FUNCTION__) [\(url)] -> `\(error.localizedDescription)` [\(error.domain)] [\(error.code)] `\(error.localizedFailureReason ?? String())`") + conlog("\(__FUNCTION__) [\(url)] -> `\(error.localizedDescription)` [\(error.domain)] [\(error.code)] `\(error.localizedFailureReason ?? String())` : \(error.userInfo)") if error.domain == WebKitErrorDomain && error.code == 204 { askToOpenURL(url) } // `Plug-in handled load!` video/mp4 if error.domain == NSURLErrorDomain && error.code != -999 { // 999 is thrown on stopLoading() if let window = grabWindow() { - webView.presentError(error, modalForWindow: window, delegate: nil, didPresentSelector: nil, contextInfo: nil) } + presentError(error, modalForWindow: window, delegate: nil, didPresentSelector: nil, contextInfo: nil) } } } } @@ -265,7 +250,7 @@ extension WebViewController: WKNavigationDelegate { func webView(webView: WKWebView!, didFinishNavigation navigation: WKNavigation!) { let title = webView.title ?? String() let url = webView.URL ?? NSURL(string:"")! - webView.conlog("\(__FUNCTION__) \"\(title)\" [\(url)]") + conlog("\(__FUNCTION__) \"\(title)\" [\(url)]") } func webView(webView: WKWebView!, createWebViewWithConfiguration configuration: WKWebViewConfiguration, forNavigationAction navigationAction: WKNavigationAction, @@ -279,12 +264,12 @@ extension WebViewController: WKNavigationDelegate { //^tgt is given as a string in JS and WKWebView synthesizes a WKFrameInfo from it _IF_ it matches an iframe title in the DOM // otherwise == nil - webView.conlog("JS@\(navigationAction.sourceFrame?.request.URL ?? String()) window.open(\(url), \(tgt))") + conlog("JS@\(navigationAction.sourceFrame?.request.URL ?? String()) window.open(\(url), \(tgt))") if jsruntime.delegate.tryFunc("decideWindowOpenForURL", url.description) { return nil } if url.description.isEmpty { - webView.conlog("url-less JS popup request!") + conlog("url-less JS popup request!") } else if (windowFeatures.allowsResizing ?? 0) == 1 { if let window = grabWindow() { var newframe = CGRect( @@ -293,19 +278,19 @@ extension WebViewController: WKNavigationDelegate { width: CGFloat(windowFeatures.width ?? window.frame.size.width as NSNumber), height: CGFloat(windowFeatures.height ?? window.frame.size.height as NSNumber) ) - webView.conlog("adjusting window to match window.open() call with size settings: origin,size[\(newframe)]") + conlog("adjusting window to match window.open() call with size settings: origin,size[\(newframe)]") window.setFrame(newframe, display: true) } if let wvc = WebViewController(agent: webView._customUserAgent, configuration: configuration) { browser?.newTab(wvc) wvc.gotoURL(url) - webView.conlog("window.open() completed for url[\(url)])") + conlog("window.open() completed for url[\(url)])") return wvc.view as? WKWebView// window.open() -> Window() } } else if tgt.description.isEmpty { //target-less request, assuming == 'top' webView.loadRequest(navigationAction.request) } else { - webView.conlog("redirecting JS popup to system browser") + conlog("redirecting JS popup to system browser") askToOpenURL(url) } @@ -313,7 +298,7 @@ extension WebViewController: WKNavigationDelegate { } func _webViewWebProcessDidCrash(webView: WKWebView) { - webView.conlog("WebContent process crashed; reloading") + conlog("\(__FUNCTION__) reloading page") webView.reload() } } diff --git a/sites/Hangouts/app.js b/sites/Hangouts/app.js index c26e6b2..699ee9a 100644 --- a/sites/Hangouts/app.js +++ b/sites/Hangouts/app.js @@ -1,5 +1,6 @@ // https://webrtchacks.com/hangout-analysis-philipp-hancke/ var lastLaunchedURL = ''; +var hangoutsTab; var delegate = {}; // our delegate to receive events from the webview app @@ -28,14 +29,11 @@ delegate.launchURL = function(url) { // $osx.openURL(/[sms|hangouts|tel]:.*/) ca // http://handleopenurl.com/scheme/hangouts // https://productforums.google.com/forum/#!topic/hangouts/-FgLeX50Jck //xssRoster(["inputAddress", addr, [' ','\r']]); - //$.firstTabEvalJS("xssRoster(['inputAddress', "+addr+", ['.','\b','\r']]);"); //calls into AppTab[1]:notifier.js + //xssRoster(['inputAddress', "+addr+", ['.','\b','\r']]);"); // https://developer.apple.com/library/ios/featuredarticles/iPhoneURLScheme_Reference/Introduction/Introduction.html#//apple_ref/doc/uid/TP40007899 case 'sms': - //$.firstTabEvalJS("xssRoster(['inputAddress', '"+addr+"']);"); //calls into AppTab[1]:notifier.js lastLaunchedURL = url; // in case app isn't ready to handle url immediately, will get pulled from var after Ready event - $browser.firstTabEvalJS("xssRoster(['inputAddress', '"+addr+"'], ['openSMS']);"); //calls into AppTab[1]:notifier.js - //$.firstTabEvalJS("xssRoster(['openSMS']);"); //wait for button to show - //$.firstTab().frame('gtn-roster-iframe-id-b').evalJS("inputAddress("+addr+");"); + hangoutsTab.evalJS("xssRoster(['inputAddress', '"+addr+"'], ['openSMS']);"); //calls into AppTab[1]:notifier.js break; case 'tel': //$.newAppTab("https://plus.google.com/hangouts/_/?hl=en&hpn="+addr+"&hip="+addr+"&hnc=0"); @@ -61,7 +59,10 @@ delegate.handleClickedNotification = function(from, url, msg) { return true; }; -delegate.unhideApp = function(msg) { $.unhideApp(); }; +delegate.unhideApp = function(msg) { + $browser.tabSelected = hangoutsTab; + $browser.unhideApp(); +}; //addEventListener('HangoutsRosterReady', function(e){...}, false); ?? delegate.HangoutsRosterReady = function(msg) { // notifier.js will call this when roster div is created @@ -80,12 +81,7 @@ delegate.AppFinishedLaunching = function() { // OSX Yosemite safari recognizes and launches sms and tel links to any associated app // https://github.com/WebKit/webkit/blob/ce77bdb93dbd24df1af5d44a475fe29b5816f8f9/Source/WebKit2/UIProcess/mac/WKActionMenuController.mm#L691 -// $browser.newTab("https://plus.google.com/hangouts", { -// 'postinject': ["notifier"], -// 'preinject': ["styler"], -// 'handlers': ['receivedHangoutsMessage','unhideApp','HangoutsRosterReady'] -// }); - $browser.newTab({ + hangoutsTab = $browser.newTab({ 'url': "https://plus.google.com/hangouts", 'postinject': ["notifier"], 'preinject': ["styler"],