diff --git a/Makefile b/Makefile index c736c2a..c395c1d 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,16 @@ #!/usr/bin/make -f -$(shell [ -d build ] || mkdir build) appdir := apps apps := Hangouts.app Trello.app Digg.app Vine.app +appexec := build/MacOS/MacPin #apps := $(addsuffix .app, $(basename $(wildcard sites/*))) #apps := $(patsubst %, %.app, $(basename $(wildcard sites/*))) debug ?= -debug += -D APP2JSLOG -D SAFARIDBG -D WK2LOG -D DBGMENU +debug += -D APP2JSLOG -D SAFARIDBG -D WK2LOG -D DBGMENU #^ need to make sure !release target +#verbose := -driver-print-jobs +# ^ just a dry-run +#verbose := -v all: $(apps) -exec := build/MacPin.exec VERSION := 1.1.0 LAST_TAG != git describe --abbrev=0 --tags @@ -17,33 +19,30 @@ REPO := MacPin ZIP := $(REPO).zip GH_RELEASE_JSON = '{"tag_name": "v$(VERSION)","target_commitish": "master","name": "v$(VERSION)","body": "MacPin build of version $(VERSION)","draft": false,"prerelease": false}' -swiftc := xcrun -sdk macosx swiftc +swiftc := xcrun -sdk macosx swiftc $(verbose) sdkpath := $(shell xcrun --show-sdk-path --sdk macosx) -frameworks := -F $(sdkpath)/System/Library/Frameworks -F build -modincdirs := -I build -L build -I objc -bridgehdrs := $(wildcard modules/*/objc-bridging.h) -bridgeopt := $(addprefix -import-objc-header , $(bridgehdrs)) +frameworks := -F $(sdkpath)/System/Library/Frameworks -F build -F build/Frameworks +modincdirs := -I build -L build -L build/Frameworks +modincdirs += $(addprefix -I ,$(patsubst %/,%,$(dir $(wildcard modules/*/module.modulemap)))) -modules/%/objc-bridging.h: ; #ignore missing bridge headers -build/%.objc-bridging.o: modules/%/objc-bridging.h - env bash objc/bridge-maker.sh $< $@ +#build/%.appex +#$(swiftc) -application-extension +# https://developer.apple.com/library/mac/documentation/General/Reference/InfoPlistKeyReference/Articles/SystemExtensionKeys.html#//apple_ref/doc/uid/TP40014212-SW15 -build/%.exec: build/%.o build/%.objc-bridging.o +build/MacOS/%: execs/%.swift build/Frameworks/lib%.dylib + install -d $(@D) $(swiftc) $(debug) $(frameworks) $(modincdirs) \ - -Xlinker -rpath -Xlinker @executable_path/ \ - -emit-executable -o $@ $< $(wildcard build/$*.objc-bridging.o) + -Xlinker -rpath -Xlinker @loader_path/../Frameworks \ + -module-link-name $* -module-name $*.MainExec \ + -emit-executable -o $@ $< -build/%.o: modules/%/main.swift build/lib%.dylib - $(swiftc) $(debug) $(frameworks) $(bridgeopt) $(modincdirs) \ - -l$* -module-link-name $* -module-name $*Main \ - -emit-object -o $@ $< - -build/lib%.dylib build/%.swiftmodule build/%.swiftdoc: modules/%/*.swift - $(swiftc) $(debug) $(frameworks) $(modincdirs) $(bridgeopt) \ - -emit-library -o build/lib$*.dylib \ +build/Frameworks/lib%.dylib build/%.swiftmodule build/%.swiftdoc: modules/%/*.swift + install -d $(@D) + $(swiftc) $(debug) $(frameworks) $(modincdirs) \ + -emit-library -o $@ \ -Xlinker -install_name -Xlinker @rpath/lib$*.dylib \ -emit-module -module-name $* -module-link-name $* -emit-module-path build/$*.swiftmodule \ - $(filter-out $(wildcard modules/*/main.swift), $+) + $+ build/%.a: build/%.o ar rcs $@ $< @@ -59,52 +58,53 @@ icons/%.icns: icons/WebKit.icns # https://developer.apple.com/library/mac/documentation/General/Reference/InfoPlistKeyReference/Articles/AboutInformationPropertyListFiles.html # https://developer.apple.com/library/mac/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html -%.app: $(exec) icons/%.icns sites/% entitlements.plist +%.app: $(appexec) icons/%.icns sites/% entitlements.plist build/Frameworks/lib$(notdir $(appexec)).dylib python2.7 -m bundlebuilder \ --name=$* \ - --bundle-id=com.github.kfix.macpin.$* \ + --bundle-id=com.github.kfix.$(notdir $(appexec)).$* \ --builddir=$(appdir) \ - --executable=$(exec) \ + --executable=$(appexec) \ --iconfile=icons/$*.icns \ --file=sites/$*:Contents/Resources \ + $(addprefix --lib=,$(filter %.dylib,$+)) \ build - #install -d $(appdir)/$*.app/Contents/MacOS/modules - #install $(modobjs) $(appdir)/$*.app/Contents/MacOS/modules - install build/lib*.dylib $(appdir)/$*.app/Contents/MacOS plutil -replace CFBundleShortVersionString -string "$(VERSION)" $(appdir)/$*.app/Contents/Info.plist plutil -replace CFBundleVersion -string "0.0.0" $(appdir)/$*.app/Contents/Info.plist plutil -replace NSHumanReadableCopyright -string "built $(shell date) by $(shell id -F)" $(appdir)/$*.app/Contents/Info.plist - #true = NSSupportsAutomaticTermination NSSupportsSuddenTermination + plutil -replace NSSupportsAutomaticTermination -bool true $(appdir)/$*.app/Contents/Info.plist + plutil -replace NSSupportsSuddenTermination -bool true $(appdir)/$*.app/Contents/Info.plist -codesign -s - -f --entitlements entitlements.plist $(appdir)/$*.app && codesign -dv $(appdir)/$*.app -asctl container acl list -file $(appdir)/$*.app +%.appex: %.app appex.plist +# --bundle-id=com.github.kfix.macpin.$*.Extension + install: $(apps) cd $(appdir); cp -R $+ ~/Applications clean: - -rm $(exec) *.o *.d *.zip - -cd build && rm *.o *.a *.dylib *.d *.swiftmodule *.swiftdoc *.exec + -rm -rf build -cd $(appdir) && rm -rf *.app uninstall: - -defaults delete $(exec) - -rm -rf ~/Library/Caches/com.github.kfix.$(exec).* ~/Library/WebKit/com.github.kfix.$(exec).* ~/Library/WebKit/$(exec) + -defaults delete $(appexec) + -rm -rf ~/Library/Caches/com.github.kfix.$(notdir $(appexec)).* ~/Library/WebKit/com.github.kfix.$(notdir $(appexec)).* ~/Library/WebKit/$(notdir $(appexec)) -rm -rf ~/Library/Saved\ Application\ State/com.github.kfix.macpin.* -rm -rf ~/Library/Preferences/com.github.kfix.macpin.* -cd ~/Applications; rm -rf $(apps) - -defaults read com.apple.spaces app-bindings | grep com.githib.kfix.macpin. + -defaults read com.apple.spaces app-bindings | grep com.github.kfix.macpin. # open ~/Library/Preferences/com.apple.spaces.plist -test: $(exec) - #-defaults delete $(exec) +test: $(appexec) + #-defaults delete $(notdir appexec) $< http://browsingtest.appspot.com -doc: $(exec) +doc: $(appexec) echo ":print_module MacPin" | xcrun swift $(modincdirs) -deprecated-integrated-repl -nightly: $(exec) DYLD_FRAMEWORK_PATH=/Volumes/WebKit/WebKit.app/Contents/Frameworks/10.10 - -defaults delete $(exec) - -rm -rf ~/Library/WebKit/$(exec) +nightly: $(appexec) DYLD_FRAMEWORK_PATH=/Volumes/WebKit/WebKit.app/Contents/Frameworks/10.10 + -defaults delete $(notdir $(appexec)) + -rm -rf ~/Library/WebKit/$(notdir $(appexec)) $< tag: @@ -120,5 +120,5 @@ release: clean $(apps) $(ZIP) dload=$$(curl --fail -X POST -H "Content-Type: application/gzip" --data-binary "@$(ZIP)" "$$posturl=$(ZIP)&access_token=$(GITHUB_ACCESS_TOKEN)" | jq -r .browser_download_url | sed 's/[\{\}]//g') && \ echo "$(REPO) now available for download at $$dload" -.PRECIOUS: icons/%.icns $(appdir)/%.app build/lib%.dylib +.PRECIOUS: icons/%.icns $(appdir)/%.app build/Frameworks/lib%.dylib .PHONY: clean install uninstall test all tag release nightly doc diff --git a/entitlements.plist b/entitlements.plist index 93b7912..5863db7 100644 --- a/entitlements.plist +++ b/entitlements.plist @@ -1,9 +1,33 @@ - + + - - - com.apple.security.get-task-allow - - + + com.apple.security.application-groups + + com.github.kfix.macpin + + + + com.apple.security.get-task-allow + + + diff --git a/modules/MacPin/main.swift b/execs/MacPin.swift similarity index 79% rename from modules/MacPin/main.swift rename to execs/MacPin.swift index 6ad703f..65a5c57 100644 --- a/modules/MacPin/main.swift +++ b/execs/MacPin.swift @@ -1,3 +1,4 @@ +import Cocoa import MacPin // gotta set these before MacPin()->NSWindow() @@ -11,8 +12,6 @@ NSUserDefaults.standardUserDefaults().setInteger(0, forKey: "__WebInspectorPageG let app = NSApplication.sharedApplication() //connect to CG WindowServer, always accessible as var:NSApp app.setActivationPolicy(.Regular) //lets bundle-less binaries (`make test`) get app menu and fullscreen button -//let applicationDelegate = MacPin() -let applicationDelegate = AppDelegate() +let applicationDelegate = MacPin.AppDelegate() app.delegate = applicationDelegate -NSAppleEventManager.sharedAppleEventManager().setEventHandler(applicationDelegate, andSelector: "handleGetURLEvent:replyEvent:", forEventClass: AEEventClass(kInternetEventClass), andEventID: AEEventID(kAEGetURL)) //handle `open url` app.run() diff --git a/modules/MacPin/AppDelegate.swift b/modules/MacPin/AppDelegate.swift index 8350e7f..5510290 100644 --- a/modules/MacPin/AppDelegate.swift +++ b/modules/MacPin/AppDelegate.swift @@ -1,5 +1,6 @@ import Cocoa import ObjectiveC +import WebKitPrivates extension NSMenu { func easyAddItem(title: String , _ action: String , _ key: String? = nil, _ keyflags: [NSEventModifierFlags] = []) { @@ -19,10 +20,10 @@ extension NSMenu { } //@NSApplicationMain // doesn't work without NIBs, using main.swift instead :-( -public class AppDelegate: NSObject, NSApplicationDelegate { +public class AppDelegate: NSObject { - public var browserController = BrowserViewController() - public var windowController: WindowController + var browserController = BrowserViewController() + var windowController: WindowController override public init() { browserController.title = nil @@ -33,6 +34,18 @@ public class AppDelegate: NSObject, NSApplicationDelegate { func conlog(msg: String) { browserController.conlog(msg) } + func handleGetURLEvent(event: NSAppleEventDescriptor!, replyEvent: NSAppleEventDescriptor?) { + if let urlstr = event.paramDescriptorForKeyword(AEKeyword(keyDirectObject))!.stringValue { + warn("\(__FUNCTION__) `\(urlstr)` -> jsruntime.delegate.launchURL()") + jsruntime.delegate.tryFunc("launchURL", urlstr) + //replyEvent == ?? + } + //replyEvent == ?? + } +} + +extension AppDelegate: NSApplicationDelegate { + //public func applicationDockMenu(sender: NSApplication) -> NSMenu? public func applicationShouldOpenUntitledFile(app: NSApplication) -> Bool { return false } @@ -125,7 +138,7 @@ public class AppDelegate: NSObject, NSApplicationDelegate { windowController.window?.initialFirstResponder = browserController.view // should defer to selectedTab.initialFirstRepsonder, which is always a webview windowController.showWindow(self) - NSAppleEventManager.sharedAppleEventManager().setEventHandler(self, andSelector: "handleGetURLEvent:replyEvent:", forEventClass: AEEventClass(kInternetEventClass), andEventID: AEEventID(kAEGetURL)) //handle `open url` + NSAppleEventManager.sharedAppleEventManager().setEventHandler(self, andSelector: "handleGetURLEvent:replyEvent:", forEventClass: AEEventClass(kInternetEventClass), andEventID: AEEventID(kAEGetURL)) //route registered url scehems } //public func application(theApplication: NSApplication, openFile filename: String) -> Bool {} diff --git a/modules/MacPin/AppScriptRuntime.swift b/modules/MacPin/AppScriptRuntime.swift index 6cf6202..6bd1cb8 100644 --- a/modules/MacPin/AppScriptRuntime.swift +++ b/modules/MacPin/AppScriptRuntime.swift @@ -2,9 +2,12 @@ Joey Korkames (c)2015 Special thanks to Google for its horrible Hangouts App on Chrome for OSX :-) */ +// somehow get a JXA bridge working? +// https://developer.apple.com/library/mac/releasenotes/InterapplicationCommunication/RN-JavaScriptForAutomation/index.html let jsruntime = AppScriptRuntime() //singleton +import Cocoa import JavaScriptCore // https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/API public extension JSValue { @@ -24,6 +27,7 @@ public extension JSValue { func registerURLScheme(scheme: String) func postNotification(title: String, _ subtitle: String?, _ msg: String) func openURL(urlstr: String, _ app: String?) + func sleep(secs: Double) } public class AppScriptRuntime: NSObject, OSXScriptExports { @@ -40,6 +44,22 @@ public class AppScriptRuntime: NSObject, OSXScriptExports { //func log(msg: String) { } + func sleep(secs: Double) { + let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(Double(NSEC_PER_SEC) * secs)) + dispatch_after(delayTime, dispatch_get_main_queue()){} + } + +/* + func delay(delay:Double, closure:()->()) { + dispatch_after( + dispatch_time( + DISPATCH_TIME_NOW, + Int64(delay * Double(NSEC_PER_SEC)) + ), + dispatch_get_main_queue(), closure) + } +*/ + 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 @@ -79,15 +99,6 @@ public class AppScriptRuntime: NSObject, OSXScriptExports { } } - func handleGetURLEvent(event: NSAppleEventDescriptor!, replyEvent: NSAppleEventDescriptor?) { - if let urlstr = event.paramDescriptorForKeyword(AEKeyword(keyDirectObject))!.stringValue { - warn("\(__FUNCTION__) `\(urlstr)` -> jsruntime.delegate.launchURL()") - delegate.tryFunc("launchURL", urlstr) - //replyEvent == ?? - } - //replyEvent == ?? - } - // 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 b0e2cbb..8f4c77b 100644 --- a/modules/MacPin/BrowserViewController.swift +++ b/modules/MacPin/BrowserViewController.swift @@ -20,7 +20,7 @@ import JavaScriptCore // https://github.com/WebKit/webkit/tree/master/Source/Ja public class TabViewController: NSTabViewController { // & NSViewController let URLBox = "Current URL" - let urlboxVC = URLBoxController(urlbox: NSTextField())! + let urlbox = URLBoxController(webViewController: nil) let ShareButton = "Share URL" let NewTabButton = "Open New Tab" var cornerRadius = CGFloat(0.0) // increment above 0.0 to put nice corners on the window FIXME userDefaults @@ -38,9 +38,7 @@ public class TabViewController: NSTabViewController { // & NSViewController override public func tabView(tabView: NSTabView, didSelectTabViewItem tabViewItem: NSTabViewItem) { super.tabView(tabView, didSelectTabViewItem: tabViewItem) - urlboxVC.representedObject = tabViewItem.viewController // KVC bind webview title and URL to urlbox - urlboxVC.nextResponder = tabViewItem.view // ensure all menu bindings will walk responder tree starting from webview - urlboxVC.view.nextKeyView = tabViewItem.view // TABbing off the urlbox will move focus to selected webview + urlbox.representedObject = tabViewItem.viewController if let window = view.window { if let view = tabViewItem.view { @@ -74,7 +72,9 @@ 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, NSToolbarFlexibleSpaceItemIdentifier] + tabs! + //return [ShareButton, NewTabButton, NSToolbarFlexibleSpaceItemIdentifier] + tabs! + //return [NSToolbarFlexibleSpaceItemIdentifier, ShareButton] + tabs! + [NewTabButton, NSToolbarFlexibleSpaceItemIdentifier] + return [NSToolbarFlexibleSpaceItemIdentifier, ShareButton, NSToolbarFlexibleSpaceItemIdentifier] + tabs! + [NSToolbarFlexibleSpaceItemIdentifier, NewTabButton, NSToolbarFlexibleSpaceItemIdentifier] //tabviewcontroller somehow remembers where tabs! was and keeps putting new tabs in that position } @@ -121,7 +121,7 @@ public class TabViewController: NSTabViewController { // & NSViewController ti.visibilityPriority = NSToolbarItemVisibilityPriorityLow ti.minSize = CGSize(width: 70, height: 24) ti.maxSize = CGSize(width: 600, height: 24) - ti.view = urlboxVC.view + ti.view = urlbox.view return ti default: @@ -261,11 +261,11 @@ public class BrowserViewController: TabViewController, BrowserScriptExports { func toggleTransparency() { isTransparent = !isTransparent } + func loadSiteApp() { jsruntime.loadSiteApp() } func editSiteApp() { NSWorkspace.sharedWorkspace().openFile(NSBundle.mainBundle().resourcePath!) } func newTabPrompt() { if let wvc = newTab(NSURL(string: "about:blank")!) { - //if let id: AnyObject = tabViewItemForViewController(wvc)?.identifier { tabSelected = id } tabSelected = wvc wvc.gotoButtonClicked(nil) } @@ -318,7 +318,6 @@ public class BrowserViewController: TabViewController, BrowserScriptExports { } return nil } - func newTab(urlstr: String) { if let url = NSURL(string: urlstr) { newTab(url) } } func newTab(url: NSURL) -> WebViewController? { if let wvc = WebViewController(agent: nil) { newTab(wvc) @@ -344,6 +343,7 @@ public class BrowserViewController: TabViewController, BrowserScriptExports { tab.initialFirstResponder = subvc.view addTabViewItem(tab) } + func newTab(urlstr: String) { if let url = NSURL(string: urlstr) { newTab(url) } } func conlog(msg: String) { // _ webview: WKWebView? = nil warn(msg) diff --git a/modules/MacPin/Exts.swift b/modules/MacPin/Exts.swift index ffe2273..558a4ff 100644 --- a/modules/MacPin/Exts.swift +++ b/modules/MacPin/Exts.swift @@ -1,5 +1,6 @@ import Cocoa import WebKit +import WebKitPrivates import JavaScriptCore func warn(msg:String) { NSFileHandle.fileHandleWithStandardError().writeData((msg + "\n").dataUsingEncoding(NSUTF8StringEncoding)!) } diff --git a/modules/MacPin/URLBoxController.swift b/modules/MacPin/URLBoxController.swift index 96aca74..029cb76 100644 --- a/modules/MacPin/URLBoxController.swift +++ b/modules/MacPin/URLBoxController.swift @@ -8,15 +8,19 @@ public extension WKWebView { import Cocoa class URLBoxController: NSViewController { - required init?(coder: NSCoder) { super.init(coder: coder) } - + let urlbox = NSTextField() //let progbar = NSProgressIndicator(frame: CGRectZero) + override var representedObject: AnyObject? { didSet { //KVC to copy updates to webviews url & title (user navigations, history.pushState(), window.title=) view.bind(NSToolTipBinding, toObject: self, withKeyPath: "representedObject.view.title", options: nil) view.bind(NSValueBinding, toObject: self, withKeyPath: "representedObject.view.URL", options: nil) view.bind(NSFontItalicBinding, toObject: self, withKeyPath: "representedObject.view.isLoading", options: nil) //progbar.bind("doubleValue", toObject: self, withKeyPath: "representedObject.view.estimatedProgress", options: nil) + if let focusObj = representedObject as? NSViewController { + nextResponder = focusObj + view.nextKeyView = focusObj.view + } } willSet(newro) { view.unbind(NSToolTipBinding) @@ -26,8 +30,18 @@ class URLBoxController: NSViewController { } } - init?(urlbox: NSTextField) { //prepareForReuse()? - super.init(nibName: nil, bundle:nil) + // NIBless + required init?(coder: NSCoder) { super.init(coder: coder) } + override init?(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) } + override init() { super.init() } + override func loadView() { view = urlbox } + + convenience init(webViewController: WebViewController?) { + self.init() + preferredContentSize = CGSize(width: 600, height: 24) //app hangs on view presentation without this! + if let webVC = webViewController { representedObject = webVC } + + // prepareForReuse() {...} ? urlbox.bezelStyle = .RoundedBezel urlbox.drawsBackground = false urlbox.textColor = NSColor.controlTextColor() @@ -36,7 +50,7 @@ class URLBoxController: NSViewController { //urlbox.editable = false //urlbox.menu = NSMenu //ctrl-click context menu - //ti.minSize = CGSize(width: 70, height: 24) + urlbox.delegate = self if let cell = urlbox.cell() as? NSTextFieldCell { cell.placeholderString = "Navigate to URL" @@ -47,14 +61,9 @@ class URLBoxController: NSViewController { cell.sendsActionOnEndEditing = false //only send action when Return is pressed cell.usesSingleLineMode = true cell.focusRingType = .None - //cell.representedObject - //cell.currentEditor()?.delegate = blah - // do live validation of input URL before Enter is pressed - // http://stackoverflow.com/questions/3605438/validate-decimal-max-min-values-in-a-nstextfield?rq=1 + //cell.currentEditor()?.delegate = self // do live validation of input URL before Enter is pressed } - view = urlbox //view.addSubview(progbar) - preferredContentSize = CGSize(width: 600, height: 24) } override func viewDidLoad() { @@ -78,22 +87,33 @@ class URLBoxController: NSViewController { func userEnteredURL() { var urlstr = (view as NSTextField).stringValue if let url = validateURL(urlstr) { - //would be cool if I could just kick up gotoURL up the responder chain? - if let wvc = representedObject? as? WebViewController { + if let wvc = representedObject? as? WebViewController { //would be cool if I could just kick up gotoURL to nextResponder as NSResponder view.window?.makeFirstResponder(wvc.view) - wvc.gotoURL(url) + wvc.gotoURL(url as NSURL) //view.addSubview(progbar) //wait for isLoaded == true presentingViewController?.dismissViewController(self) //if a thoust art a popover, close thyself } } else { warn("\(__FUNCTION__): invalid url `\(urlstr)` was requested!") + //NSBeep() } } override func cancelOperation(sender: AnyObject?) { presentingViewController?.dismissViewController(self) } //if a thoust art a popover, close thyself } +extension URLBoxController: NSTextFieldDelegate { + //func textShouldBeginEditing(textObject: NSText) -> Bool { return true } //allow editing + //func textDidChange(aNotification: NSNotification) + func textShouldEndEditing(textObject: NSText) -> Bool { //user attempting to focus out of field + if let url = validateURL(textObject.string ?? "") { return true } //allow focusout + warn("\(__FUNCTION__): invalid url entered: \(textObject.string)") + return false //NSBeep()s, keeps focus + } + //func textDidEndEditing(aNotification: NSNotification) { } +} + extension URLBoxController: NSPopoverDelegate { func popoverWillShow(notification: NSNotification) { //view.frame = NSRect(x:0, y:0, width: 600, height: 24) diff --git a/modules/MacPin/WebViewController.swift b/modules/MacPin/WebViewController.swift index 43c570d..ce04a56 100644 --- a/modules/MacPin/WebViewController.swift +++ b/modules/MacPin/WebViewController.swift @@ -1,7 +1,10 @@ +import WebKit +import WebKitPrivates + @objc protocol WebViewScriptExports : JSExport { // 'var tab = $browser.newTab({...})' in app.js func close() func evalJS(js: String) - func gotoURL(urlstr: String) + func loadURL(urlstr: String) -> Bool } class WebViewController: NSViewController { @@ -175,24 +178,28 @@ extension WebViewController: WebViewScriptExports { // AppGUI / ScriptRuntime fu webview.loadRequest(NSURLRequest(URL: url)) } } - func gotoURL(urlstr: String) { gotoURL(NSURL(string: urlstr)!) } + func loadURL(urlstr: String) -> Bool { // overloading gotoURL is crashing Cocoa selectors + if let url = NSURL(string: urlstr) { + gotoURL(url as NSURL) + return true + } + return false // tell JS we were given a malformed URL + } func gotoButtonClicked(sender: AnyObject?) { - var urlboxVC = URLBoxController(urlbox: NSTextField())! - urlboxVC.representedObject = self // KVC bind webview title and URL to urlbox - urlboxVC.nextResponder = self.view // ensure all menu bindings will walk responder tree starting from webview - urlboxVC.view.nextKeyView = self.view // TABbing off the urlbox will move focus to selected webview //FIXME not working + let urlbox = URLBoxController(webViewController: self) + if let btn = sender? as? NSView { - presentViewController(urlboxVC, asPopoverRelativeToRect: btn.bounds, ofView: btn, preferredEdge:NSMinYEdge, behavior: .Semitransient) + presentViewController(urlbox, asPopoverRelativeToRect: btn.bounds, ofView: btn, preferredEdge:NSMinYEdge, behavior: .Semitransient) } else { - presentViewControllerAsSheet(urlboxVC) //Keyboard shortcut + presentViewControllerAsSheet(urlbox) //Keyboard shortcut } } func askToOpenURL(url: NSURL) { if let window = grabWindow() { if let opener = NSWorkspace.sharedWorkspace().URLForApplicationToOpenURL(url) { - conlog("prompting to externally open URL: [\(url)]") + conlog("\(__FUNCTION__): [\(url)]") let alert = NSAlert() alert.messageText = "Open using App:\n\(opener)" alert.addButtonWithTitle("OK") diff --git a/modules/MacPin/WebViewDelegates.swift b/modules/MacPin/WebViewDelegates.swift index 4908cc6..f30b732 100644 --- a/modules/MacPin/WebViewDelegates.swift +++ b/modules/MacPin/WebViewDelegates.swift @@ -229,7 +229,7 @@ extension WebViewController: WKNavigationDelegate { if navigationResponse.canShowMIMEType { decisionHandler(.Allow) } else { - conlog("webView cannot render requested MIME-type:\(mime) @ \(url)") + conlog("\(__FUNCTION__) cannot render requested MIME-type:\(mime) @ \(url)") if !jsruntime.delegate.tryFunc("handleUnrenderableMIME", mime, url.description, fn) { askToOpenURL(url) } decisionHandler(.Cancel) } diff --git a/modules/MacPin/WindowController.swift b/modules/MacPin/WindowController.swift index 4db3ba8..2f406e5 100644 --- a/modules/MacPin/WindowController.swift +++ b/modules/MacPin/WindowController.swift @@ -8,7 +8,7 @@ public class WindowController: NSWindowController, NSWindowDelegate { // take care of awakeFromNib() & windowDidLoad() tasks, which are not called for NIBless windows super.init(window: window) if let window = window { - window.collectionBehavior = .FullScreenPrimary | .ParticipatesInCycle | .Managed | .CanJoinAllSpaces | .FullScreenAuxiliary //| .MoveToActiveSpace + window.collectionBehavior = .FullScreenPrimary | .ParticipatesInCycle | .Managed | .CanJoinAllSpaces window.styleMask |= NSUnifiedTitleAndToolbarWindowMask window.movableByWindowBackground = true window.backgroundColor = NSColor.whiteColor() @@ -25,6 +25,7 @@ public class WindowController: NSWindowController, NSWindowDelegate { //window.registerForDraggedTypes([NSPasteboardTypeString,NSURLPboardType,NSFilenamesPboardType]) window.cascadeTopLeftFromPoint(NSMakePoint(20,20)) window.delegate = self + window.restorable = true window.restorationClass = AppDelegate.self } shouldCascadeWindows = false // messes with Autosave diff --git a/objc/WKPreferences+Privates.h b/modules/WebKitPrivates/WKPreferences+Privates.h similarity index 100% rename from objc/WKPreferences+Privates.h rename to modules/WebKitPrivates/WKPreferences+Privates.h diff --git a/objc/WKView+Privates.h b/modules/WebKitPrivates/WKView+Privates.h similarity index 100% rename from objc/WKView+Privates.h rename to modules/WebKitPrivates/WKView+Privates.h diff --git a/objc/WKWebView+Privates.h b/modules/WebKitPrivates/WKWebView+Privates.h similarity index 100% rename from objc/WKWebView+Privates.h rename to modules/WebKitPrivates/WKWebView+Privates.h diff --git a/modules/MacPin/objc-bridging.h b/modules/WebKitPrivates/WebKitPrivates.h similarity index 100% rename from modules/MacPin/objc-bridging.h rename to modules/WebKitPrivates/WebKitPrivates.h diff --git a/objc/WebURLsWithTitles.h b/modules/WebKitPrivates/WebURLsWithTitles.h similarity index 100% rename from objc/WebURLsWithTitles.h rename to modules/WebKitPrivates/WebURLsWithTitles.h diff --git a/objc/bridge-maker.sh b/modules/WebKitPrivates/bridge-maker.sh similarity index 100% rename from objc/bridge-maker.sh rename to modules/WebKitPrivates/bridge-maker.sh diff --git a/modules/WebKitPrivates/module.modulemap b/modules/WebKitPrivates/module.modulemap new file mode 100644 index 0000000..c40f742 --- /dev/null +++ b/modules/WebKitPrivates/module.modulemap @@ -0,0 +1,6 @@ +// http://stackoverflow.com/a/24794675/3878712 +// http://clang.llvm.org/docs/Modules.html +module WebKitPrivates { + umbrella header "WebKitPrivates.h" + export * +} diff --git a/sites/Digg/app.js b/sites/Digg/app.js index 42eebf2..da1a85b 100644 --- a/sites/Digg/app.js +++ b/sites/Digg/app.js @@ -1,23 +1,38 @@ var lastLaunchedURL = ''; var delegate = {}; // our delegate to receive events from the osx app +var readerTab; delegate.launchURL = function(url) { - feed = url.replace(/^(rss|feed|http|https):\/*/,''); - feed = feed.replace(/^(rss|feed|http|https):\/*/,''); //yup, safari double-schemes correctly MIMEd URLs on its own + $browser.conlog("app.js: launching " + url); + feed = url.replace(/^(rss|feed|http|https):\/*/,''); // strip off the scheme + feed = feed.replace(/^(rss|feed|http|https):\/*/,''); // strip off the second scheme that safari adds to correctly MIMEd URLs on its own + feed = encodeURIComponent(feed); + addFeedToDigg = "http://digg.com/reader/search/" + feed; //WTF: http://en.wikipedia.org/wiki/Feed_URI_scheme - $browser.newTab({'url':"http://digg.com/reader/search/"+encodeURIComponent(feed), 'postinject':["styler"]}); - //FIXME: eval some JS to the Digg app to directly input URL? + //$browser.newTab({'url':addFeedToDigg, 'postinject':["styler"]}); + + $browser.tabSelected = readerTab; + readerTab.loadURL(addFeedToDigg); + + //readerTab.evalJS("location.href = '"+addFeedToDigg+"';"); + //readerTab.evalJS("history.pushState({}, 'Search for feeds to add', '"+addFeedToDigg+"');"); + // _, require, requirejs, and Backbone.Router.navigate are all present ... + // Backbone.Router.prototype.navigate("search/:feed", {trigger: true}); + + //readerTab.evalJS("$('#tooltip-subscription-add-form-input')[0].value = '"+feed+"';"); + //readerTab.evalJS("$('#tooltip-subscription-add-form')[0].submit();"); } delegate.AppFinishedLaunching = function() { $osx.registerURLScheme('feed'); $osx.registerURLScheme('rss'); + readerTab = $browser.newTab({'url':"http://digg.com/reader", 'postinject':["styler"]}); + + // need a app ready event if (lastLaunchedURL != '') { delegate.launchURL(lastLaunchedURL); lastLaunchedURL = ''; - } else { - $browser.newTab({'url':"http://digg.com/reader", 'postinject':["styler"]}); } } delegate; //return delegate to app diff --git a/sites/Hangouts/app.js b/sites/Hangouts/app.js index 699ee9a..b57ff86 100644 --- a/sites/Hangouts/app.js +++ b/sites/Hangouts/app.js @@ -1,7 +1,8 @@ -// https://webrtchacks.com/hangout-analysis-philipp-hancke/ var lastLaunchedURL = ''; var hangoutsTab; - +var useChromeAV = true; + // if true, use Chrome's NaCL+WebRTC Hangouts client for A/V chats ( https://webrtchacks.com/hangout-analysis-philipp-hancke/ ) + // else use NPAPI GoogleVoice & Video (Vidyo) plugin in WebKit ( https://encrypted.google.com/tools/dlpage/hangoutplugin ) var delegate = {}; // our delegate to receive events from the webview app delegate.decideNavigationForClickedURL = function(url) { $osx.openURL(url); return true; } @@ -9,9 +10,10 @@ delegate.decideNavigationForMIME = function(url) { return false; } delegate.decideWindowOpenForURL = function(url) { $browser.conlog("JScore: catching window.open() " + url); if (~url.indexOf("https://plus.google.com/hangouts/_/")) { //G+ hangouts a/v chat - $osx.openURL(url, "com.google.Chrome"); // use Hangouts Pepper WebRTC plugin - //$.newAppTabWithJS(url); - //$.switchToNextTab(); + if (useChromeAV) + $osx.openURL(url, "com.google.Chrome"); + else + $browser.tabSelected = $browser.newTab({'url':url}); return true; }; //if url ^= https://talkgadget.google.com/u/0/talkgadget/_/frame @@ -30,14 +32,15 @@ delegate.launchURL = function(url) { // $osx.openURL(/[sms|hangouts|tel]:.*/) ca // https://productforums.google.com/forum/#!topic/hangouts/-FgLeX50Jck //xssRoster(["inputAddress", addr, [' ','\r']]); //xssRoster(['inputAddress', "+addr+", ['.','\b','\r']]);"); - // https://developer.apple.com/library/ios/featuredarticles/iPhoneURLScheme_Reference/Introduction/Introduction.html#//apple_ref/doc/uid/TP40007899 case 'sms': lastLaunchedURL = url; // in case app isn't ready to handle url immediately, will get pulled from var after Ready event 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"); - $osx.openURL("https://plus.google.com/hangouts/_/?hl=en&hpn="+addr+"&hip="+addr+"&hnc=0", "com.google.Chrome"); //use Chrome's NaCL+WebRTC client + if (useChromeAV) + $osx.openURL("https://plus.google.com/hangouts/_/?hl=en&hpn="+addr+"&hip="+addr+"&hnc=0", "com.google.Chrome"); //use Chrome's NaCL+WebRTC Hangouts client + else + $browser.tabSelected = $browser.newTab({"url": "https://plus.google.com/hangouts/_/?hl=en&hpn="+addr+"&hip="+addr+"&hnc=0"}); break; default: }; @@ -47,13 +50,13 @@ delegate.launchURL = function(url) { // $osx.openURL(/[sms|hangouts|tel]:.*/) ca delegate.receivedHangoutsMessage = function(msg) { // receives events from JS in 1st AppTab // -> webkit.messageHandlers.receivedHangoutMessage.postMessage([from, replyTo, msg]); - //$.postOSXNotification.apply(this, msg); // why no workie? $osx.postNotification(msg[0], msg[1], msg[2]); $browser.conlog(Date() + ' [posted osx notification] ' + msg); }; delegate.handleClickedNotification = function(from, url, msg) { $browser.conlog("JS: opening notification for: "+ [from, url, msg]); + $browser.tabSelected = hangoutsTab; lastLaunchedURL = url; // in case app isn't ready to handle url immediately, will get pulled from var after Ready event delegate.launchURL(lastLaunchedURL); return true; @@ -64,30 +67,30 @@ delegate.unhideApp = function(msg) { $browser.unhideApp(); }; -//addEventListener('HangoutsRosterReady', function(e){...}, false); ?? -delegate.HangoutsRosterReady = function(msg) { // notifier.js will call this when roster div is created - if (lastLaunchedURL != '') { //app was launched by an opened URL or a clicked notification - probably a [sms|tel]: link - $browser.unhideApp(); //user might have switched apps while waiting for roster to load - delegate.launchURL(lastLaunchedURL); - lastLaunchedURL = ''; - }; -}; - delegate.AppFinishedLaunching = function() { $osx.registerURLScheme('sms'); $osx.registerURLScheme('tel'); $osx.registerURLScheme('hangouts'); - $browser.isTransparent = true; // 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 + // https://github.com/WebKit/webkit/blob/ce77bdb93dbd24df1af5d44a475fe29b5816f8f9/Source/WebKit2/UIProcess/mac/WKActionMenuController.mm#L691 + // https://developer.apple.com/library/ios/featuredarticles/iPhoneURLScheme_Reference/Introduction/Introduction.html#//apple_ref/doc/uid/TP40007899 + $browser.isTransparent = true; hangoutsTab = $browser.newTab({ 'url': "https://plus.google.com/hangouts", 'postinject': ["notifier"], 'preinject': ["styler"], 'handlers': ['receivedHangoutsMessage','unhideApp','HangoutsRosterReady'] }); +}; +//addEventListener('HangoutsRosterReady', function(e){...}, false); ?? +delegate.HangoutsRosterReady = function(msg) { // notifier.js will call this when roster div is created + if (lastLaunchedURL != '') { //app was launched by an opened URL or a clicked notification - probably a [sms|tel]: link + $browser.unhideApp(); //user might have switched apps while waiting for roster to load + delegate.launchURL(lastLaunchedURL); + lastLaunchedURL = ''; + }; }; delegate; //return this to macpin