Retrieving parameters from a custom URL in TideSDK - macos

I'm putting together a desktop application in TideSDK and am having some trouble finding the parameters passed to the application via a custom launch URL.
The application launches when the appropriate protocol is invoked (call it aaa://), but I haven't been able to figure out how to grab the URL string. I read a couple of threads that suggested I could get the string with the Ti.API.application.getArguments() call, but it returns something odd (see below).
// launch application with aaa://some_args_here
var args = Ti.API.application.getArguments();
// returns (StaticBoundList) [ /path/to/app, "-psn_0_721072", ]
I'm not completely surprised that this doesn't seem to work, as the API documentation says the getArguments method returns a list of command line arguments.
Any insight as to how to access the launch URL would be appreciated!

have a look at window.location.search
https://developer.mozilla.org/en-US/docs/DOM/window.location

It doesn't appear that there's currently an "official" way to do this, so I came up with my own solution that seems to work pretty well (though currently its only implemented for OSX).
Searching through the TideSDK source, I found the place where the native application delegate is created. I added a URL launch handler in the app delegate, which stores the launch URL in a new app delegate member, and connected it to the API with a binding in Ti.UI.
If anybody else is interested in this functionality, or have insights as to how to accomplish this task for Windows, please get in touch!

Although I don't have your problem, just want to say the following function works fine for me.
var args = Ti.API.application.getArguments();
is it possible this is the problem of the urlprotocl registry?
my url protocol is set by this : MSDN
thus the value in "Command" is
"C:\YOUR_APP_FOLDER\YOURAPP.exe" "%1"
tested on win7 and winxp,
both successfully get the arguments.

Related

Send file URL and args to (running) macOS app via command line

I've been trying to create a way to tell my (running) macOS app to open some files and supply some additional arguments to the command.
For cold-start apps, using the
$ open MyApp.app fileA.txt --args --foo-arg
would launch the app and I would be able to inspect the --foo-arg via UserDefaults/CommandLine/ProcessInfo. However, if the app is already running, the --foo-arg is missing from UserDefaults/ProcessInfo‌/CommandLine.
I've been struggling to wrap my head around a solution here because I have a few requirements which make things a tad more difficult.
Requirements
File paths sent to app must be opened/saved with sandbox permissions
Arguments and file paths must be intercepted by app at the same time.
Potential Solutions
XPC
Some people have suggested I use XPC but after reading about it, I'm not sure how that solution might look?
Do I have to create a Launch Agent app-companion which is always running so that it can detect command line operations and pass it to my app?
How does this work with sandboxing because each process has their own permission entitlements?
Apple Script
Should I use Apple script to tell my app to open these files with arguments, thus getting around the sandboxing feature?
When opening files via AppleScript, can I save those files swell?
URL Scheme
I can register my app to have its own URL scheme but the way NSApplicationDelegate handles the incoming URLs comes in two batches. First, the URLs it can open, followed by the URL schemes or the file paths it can't open. ie:
open -a MyApp.app myapp:foo; open -a MyApp.app file.txt
I can probably make this work but it's a tad tacky and I really want to do this the right way.
A command-line tool which ingests its arguments and turns them in to Apple Events is the way to go. You can see how this works from the user's point of view by installing the BBEdit command-line tools and then running man bbedit or man bbdiff in a Terminal window.
From your command-line tool's point of view, the "interesting" parts are:
Figure out whether the application is running: +[NSRunningApplication runningApplicationsWithBundleIdentifier:] will help with that.
If the application is not running, then use -[NSWorkspaceURLForApplicationWithBundleIdentifier:] to first locate the application by bundle ID, then -[NSWorkspace launchApplicationAtURL:options:configuration:error:] to launch the application. This will return an NSRunningApplication instance, or NIL and an error. (Make sure to handle the error case.)
Using the NSRunningApplication instance obtained from either step 1 or step 2, you can now use either the NSAppleEventDescriptor APIs or the low-level AppleEvent C APIs to construct an event. (The higher-level API is probably easier to use.)
That would go something like this:
Construct a target descriptor using the processIdentifier from your running application:
targetDesc = [NSAppleEventDescriptor descriptorWithProcessIdentifier: myRunningApplication.processIdentifier;
Construct an "open documents" event, addressed to your target application:
event = [NSAppleEventDescriptor appleEventWithEventClass: kCoreEventClass eventID: kAEOpenDocuments targetDescriptor: targetDesc returnID: kAutoGenerateReturnID transactionID: kAnyTransactionID];
Note: I use kCoreEventClass/kAEOpenDocuments as an example - if you're trying to open one or more files with additional information, that's fine. If you're doing some other work, then you should invent a four-character code for an event class which is specific to your application, and a four-character event ID which is unique to the operation you're requesting.
Add the command arguments to the event. For each argument, this consists of creating an appropriate descriptor based on the argument's intrinsic type (boolean, int, string, file URL), and then adding it to the event using a keyword parameter.
(An Apple Event "keyword" is a four-character code. You can invent your own, with constraints (don't use all-lowercase, and you can use ones defined in AEDataModel.h or AERegistry.h where they fit with your needs).
For each descriptor you create, add it to the event using -[setParamDescriptor: forKeyword:]:
myURLParamDesc = [NSAppleEventDescriptor descriptorWithFileURL: myFileURL];
[event setParamDescriptor: myURLParamDesc forKey: kMyFileParamKeyword];
When you've added all of the parameters to the event, send it:
[event sendWithOptions: kAENoReply timeout: FLOAT_MAX error: &error];
On the application side, you'll need to use -[NSAppleEventManager setEventHandler: andSelector: forEventClass: andID:]. This will get called for your custom event class and ID that you invented above, at which point you can use the descriptor APIs to pull the event apart and run your operation.
Sandboxing takes care of itself: your application automatically gets a sandboxing extension for files that it's been passed via Apple Events.
Your command-line tool is not sandboxed -- it can't be, because it's run from Terminal and (potentially) other nonsandboxed apps.
However, the tool must be signed with the hardened runtime, and with com.apple.security.automation.apple-events = YES and a com.apple.security.temporary-exception.apple-events naming your application's bundle identifier, so that the tool can send Apple Events to your application.
(And the tool will need an Info.plist with an NSAppleEventsUsageDescription string.)
I've left a fair amount as an exercise for the reader; but hopefully this will get you started.

Getting parameters from applicationManager

I am basically executing the following luna-send command and trying to get those parameters from applicationManager:
luna-send -n 1 palm://com.palm.power/timeout/set '{"wakeup":true, "key":"myKey",
"uri":"palm://com.palm.applicationManager/launch","params":{"id":"com.my.app",
"params":{"test":true,"test1:true}},"in":"00:00:15"}'
After executing this command, my app gets launched by applicationManager, but I don't know how to get those params in my app. I am using enyo 2.0. I was trying to use onWindowsParamsChange handler, but ApplicationEvents is deprecated for 2.0. Can anyone help me with this?
Thanks
Under Enyo 1.0 it was enyo.windowParams. Under Enyo 2.0 I believe this functionality is gone. These parameters may be available through Cordova, but I'm not positive right now as I don't have the source handy. In any case, this was loaded from PalmSystem.launchParams so you should be able to access that.
If you're handling relaunch then you'll have a little more work to do. I think you'll need to define a Mojo.relaunch on the window object to detect when the launch parameters change.

Effective way to debug a Google Apps Script Web App

I have had some experience writing container-bound scripts, but am totally new to web apps.
How do I debug (e.g. look at variable values, step through code etc) a web app? In a container bound script it was easy, because I could set breakpoints, use the apps script debugger - how do I go about this in a web page e.g. when I execute a doPost?
In his excellent book "Google Script", James Ferreira advocates setting up your own development environment with three browser windows; one for the code, one for the live view (in Publish, Deploy as web app, you are provided with a "latest code" link that will update the live view to the latest save when it is refreshed), and one for a spreadsheet that logs errors (using try/catch wrapped around bits of code you want to keep an eye on).
In Web Apps, even the most basic debugging of variables through Logger.log() does not work!
A great solution to have at least simple variable logging available is Peter Herrmann's BetterLog for Apps Script. It allows you to log into a spreadsheet (the same as your working spreadsheet or a separate one).
Installation is very simple - just add an external resource (see the Github readme) and a single line of code to override the standard Logger object:
Logger = BetterLog.useSpreadsheet('your-spreadsheet-key-goes-here');
Remember, that the spreedsheet that you give here as a parameter will be used for the logging output and thus must be writable by anybody!
BetterLog will create a new sheet called "Log" in the given spreadsheet and will write each log call into a separate row of that sheet.
So, for me, I debug the front-end using inspector, I haven't found a way to step through code yet, but you can use 'debugger' in your javascript (along with console.log) to stop the code and check variables.
to debug the backend, what I've been doing is to write my functions like
function test_doSomething(){
payload = "{item1: 100, item2: 200}" //<- copy paste from log file
backend_doSomething(payload)
}
function backend_doSomething(payload){
Logger.log(payload)
params = JSON.parse(payload)
...
}
Then after refreshing your project on the backend, you can look at executions, grab the payload from the log file, and paste it into your test_doSomething() function.
From there, you are re-creating the call that you want to debug and you can run that, stepping through the backend code as usual.

How to obtain firefox user agent string?

I'm building an add-on for FireFox that simulates a website, but running from a local library. (If you want to know more, look here)
I'm looking for a way to get a hold of the user-agent string that FireFox would send if it were doing plain http. I'm doing the nsIProtocolHandler myself and serve my own implementation of nsIHttpChannel, so if I have a peek at the source, it looks like I'll have to do all the work myself.
Unless there's a contract/object-id on nsHttpHandler I could use to create an instance just for a brief moment to get the UserAgent? (Though I notice I'll need to call Init() because it does InitUserAgentComponents() and hope it'll get to there... And I guess the http protocol handler does the channels and handlers so there won't be a contract to nsHttpHandler directly.)
If I have a little peek over the wall I notice this globally available call ObtainUserAgentString which does just this in that parallel dimension...
Apparently Firefox changed how this was done in version 4. Have you tried:
alert(window.navigator.userAgent);
You can get it via XPCOM like this:
var httpHandler = Cc["#mozilla.org/network/protocol;1?name=http"].
getService(Ci.nsIHttpProtocolHandler);
var userAgent = httpHandler.userAgent;
If for some reason you actaully do need to use NPAPI like you suggest in your tags, you can use NPN_UserAgent to get it; however, I would be shocked if you actually needed to do that just for an extension. Most likely Anthony's answer is more what you're looking for.

How can a bookmarklet access a Firefox extension (or vice versa)

I have written a Firefox extension that catches when a particular URL is entered and does some stuff. My main app launches Firefox with this URL. The URL contains sensitive information so I don't want it being stored in the history.
I'm concerned about the case where the extension is not installed. If its not installed and Firefox gets launched with the sensitive URL, it will get stored in history and there's nothing I can do about it. So my idea is to use a bookmarklet.
I will launch Firefox with "javascript:window.location.href='pleaseinstallthisplugin.html'; sensitiveinfo='blahblah'".
If the extension is not installed they will get redirected to a page that tells them to install it and the sensitive info won't get stored in the history. If the extension IS installed it will grab the information in the sensitiveinfo variable and do its thing.
My question is, can the bookmarklet call a method in the extension to pass the sensitive info (and if so, how) or can the extension catch when javascript is being called in the bookmarklet?
How can a bookmarklet and Firefox extension communicate?
p.s. The alternative means of getting around this situation would be for my main app to launch Firefox and communicate with the extension using sockets but I am loath to do that because I've run into too many issues over the years with users with crazy firewalls blocking socket communication. I'd like to do everything without sockets if possible.
As far as I know, bookmarklets can never access chrome files (extensions).
Bookmarklets are executed in the scope of the current document, which is almost always a content document. However, if you are passing it in via the command line, it seems to work:
/Applications/Namoroka.app/Contents/MacOS/firefox-bin javascript:alert\(Components\)
Accessing Components would throw if it was not allowed, but the alert displays the proper object.
You could use unsafeWindow to inject a global. You can add a mere property so that your bookmarklet only needs to detect whether the global is defined or not, but you should know that, as far as I know, there is no way to prohibit sites in a non-bookmarklet context from also sniffing for this same global (since it may be a privacy concern to some that sites can detect whether they are using the extension). I have confirmed in my own add-on which injects a global in a manner similar to that below that it does work in a bookmarklet as well as regular site context.
If you register an nsIObserver, e.g., where content-document-global-created is the topic, and then unwrap the subject, you can inject your global (see this if you need to inject something more sophisticated like an object with methods).
Here is some (untested) code which should do the trick:
var observerService = Cc['#mozilla.org/observer-service;1'].getService(Ci.nsIObserverService);
observerService.addObserver({observe: function (subject, topic, data) {
var unsafeWindow = XPCNativeWrapper.unwrap(subject);
unsafeWindow.myGlobal = true;
}}, 'content-document-global-created', false);
See this and this if you want an apparently easier way in an SDK add-on (not sure whether SDK postMessage communication would work as an alternative but with the apparently same concern that this would be exposed to non-bookmarklet contexts (i.e., regular websites) as well).

Resources