karma-runner / karma-safari-launcher

A Karma plugin. Launcher for Safari.
MIT License
19 stars 17 forks source link

Safari 12 (mojave) asking for user feedback on opening local .html redirect #29

Open vobu opened 5 years ago

vobu commented 5 years ago

Upon running a test in karma and launching Safari via this plugin, Safari 12 on macOS mojave asks for user feedback to confirm opening the local $tmp/[safari|redirect].html

Only after explicitly confirming loading the .html, karma tests continue running.

bildschirmfoto 2018-10-05 um 17 35 02

Would have liked to PR this breaking usage change, but couldn't find any documentation on how to either provide cmd line switches to Safari or otherwise tackle this :(

FTR: env: macOS 10.14 Safari 12.0 (14606.1.36.1.9) karma-safari-launcher 1.0.0

muthu90ec commented 5 years ago

Upon running a test in karma and launching Safari via this plugin, Safari 12 on macOS mojave asks for user feedback to confirm opening the local $tmp/[safari|redirect].html

Only after explicitly confirming loading the .html, karma tests continue running.

bildschirmfoto 2018-10-05 um 17 35 02

Would have liked to PR this breaking usage change, but couldn't find any documentation on how to either provide cmd line switches to Safari or otherwise tackle this :(

FTR: env: macOS 10.14 Safari 12.0 (14606.1.36.1.9) karma-safari-launcher 1.0.0

Hi, I ran into this problem, What I think is the problem here, is that with the latest MAC release, Safari has more restrictions on opening a .html file from certain location on the machine. For eg: opeing from a tmp dir is asking for user permission. Instead if you try opening the .html file from userhome directory safari12 is happy. It seems to work on my machine give this a try. Just replace the SafariBrowser function with the code i have pasted below.

I worked around it by modifying the code in index.js.

var SafariBrowser = function(baseBrowserDecorator) {
  baseBrowserDecorator(this);

  this._start = function(url) {
    var HTML_TPL = path.normalize(__dirname + '/safari.html');
    var self = this;

    fs.readFile(HTML_TPL, function(err, data) {
      var content = data.toString().replace('%URL%', url);
      var staticHtmlPath = self._tempDir + '/redirect.html';

      fs.writeFile(staticHtmlPath, content, function(err) {
        var safariData = path.join(process.env.HOME, 'Library/Containers/com.apple.Safari/Data/redirect.html');
        fs.createReadStream(staticHtmlPath).pipe(fs.createWriteStream(safariData));
        self._execCommand(self._getCommand(), [safariData]);
      });
    });
  };
};
vobu commented 5 years ago

Hi! Good idea, didn't think of trying this out myself :) Unfortunately doesn't work, as there seems to be a permission issue on ~/Library/Containers/com.apple.Safari:

Box: ~/Library/Containers > pwd
/Users/bla/Library/Containers
Box: ~/Library/Containers > ls -al com.apple.Safari
ls: com.apple.Safari: Operation not permitted

So the node runtime can't create the file either:

errno: -1,
code: 'EPERM',
syscall: 'open',
path:
'/Users/bla/Library/Containers/com.apple.Safari/Data/redirect.html'

How do the permission on ~/Library/Containers/com.apple.Safari look on your mac? I'm on a clean install of macOS mojave.

BTW: modified your code a little for brevity:

var SafariBrowser = function (baseBrowserDecorator) {
    baseBrowserDecorator(this);

    this._start = function (url) {
        var HTML_TPL = path.normalize(__dirname + '/safari.html');
        var self = this;

        fs.readFile(HTML_TPL, function (err, data) {
            var content = data.toString().replace('%URL%', url);

            // var staticHtmlPath = self._tempDir + '/redirect.html';
            var staticHtmlPath = path.join(process.env.HOME, 'Library/Containers/com.apple.Safari/Data/redirect.html');

            fs.writeFile(staticHtmlPath, content, function (err) {
                if (err) {
                    console.error(err);
                    return false;
                }
                self._execCommand(self._getCommand(), [staticHtmlPath]);
            });
        });
    };
};
muthu90ec commented 5 years ago

Hi! Good idea, didn't think of trying this out myself :) Unfortunately doesn't work, as there seems to be a permission issue on ~/Library/Containers/com.apple.Safari:

Box: ~/Library/Containers > pwd
/Users/bla/Library/Containers
Box: ~/Library/Containers > ls -al com.apple.Safari
ls: com.apple.Safari: Operation not permitted

So the node runtime can't create the file either:

errno: -1,
code: 'EPERM',
syscall: 'open',
path:
'/Users/bla/Library/Containers/com.apple.Safari/Data/redirect.html'

How do the permission on ~/Library/Containers/com.apple.Safari look on your mac? I'm on a clean install of macOS mojave.

BTW: modified your code a little for brevity:

var SafariBrowser = function (baseBrowserDecorator) {
    baseBrowserDecorator(this);

    this._start = function (url) {
        var HTML_TPL = path.normalize(__dirname + '/safari.html');
        var self = this;

        fs.readFile(HTML_TPL, function (err, data) {
            var content = data.toString().replace('%URL%', url);

            // var staticHtmlPath = self._tempDir + '/redirect.html';
            var staticHtmlPath = path.join(process.env.HOME, 'Library/Containers/com.apple.Safari/Data/redirect.html');

            fs.writeFile(staticHtmlPath, content, function (err) {
                if (err) {
                    console.error(err);
                    return false;
                }
                self._execCommand(self._getCommand(), [staticHtmlPath]);
            });
        });
    };
};

Interesting, my HOME var has following out: cmd: printenv HOME out: /home/unixid cmd: cd /home/unixid/Library/Containers/com.apple.Safari/Data cmd: vi test.txt -> save cmd: ls -al test.txt out: -rw-r--r-- -> I have read write permission in that dir. Not sure why your id lacks privileges in dir: home/unixid/Library/Containers/com.apple.Safari/Data

gkatsev commented 5 years ago

Just tried it out and it seems to be working for me.

I just made the change here and in the STP launcher

// const staticHtmlPath = self._tempDir + '/redirect.html';
var staticHtmlPath = path.join(process.env.HOME, 'Library/Containers/com.apple.Safari/Data/redirect.html');
// const staticHtmlPath = self._tempDir + '/redirect.html';
var staticHtmlPath = path.join(process.env.HOME, 'Library/Containers/com.apple.SafariTechnologyPreview/Data/redirect.html');

Would one of you be interested in making a PR for these? Maybe we can get the maintainers to merge and release them as this is a major issue.

rmi22186 commented 5 years ago

Just came across this problem after upgrading to Mojave on my work laptop. I am having the exact same error message output as @vobu after modifying karma-safari-launcher/index.js @muthu90ec - when i CD into Users/ring/Library/Containers/com.apple.Safari/Data, I'm not able to perform any

cmd: touch test.txt out: Operation not permitted cmd: ls out: Operation not permitted

Even when I sudo, nothing works. However, when in the Finder if I manually go into /Data, and do Get Info, it says I have read/write access. Additionally, I can drag files in there with no problem.

@vobu did you ever figure this out?

muthu90ec commented 5 years ago

Just came across this problem after upgrading to Mojave on my work laptop. I am having the exact same error message output as @vobu after modifying karma-safari-launcher/index.js @muthu90ec - when i CD into Users/ring/Library/Containers/com.apple.Safari/Data, I'm not able to perform any

cmd: touch test.txt out: Operation not permitted cmd: ls out: Operation not permitted

Even when I sudo, nothing works. However, when in the Finder if I manually go into /Data, and do Get Info, it says I have read/write access. Additionally, I can drag files in there with no problem.

@vobu did you ever figure this out?

@rmi22186, Instead of /Users/ring/Li... directory could you try from /home, directory. you should have privileges in your HOME dir.

Please confirm, thanks.

rmi22186 commented 5 years ago

@muthu90ec
There doesn't seem to be anything in my root home folder. perhaps it's the way our sys admins set up our computers, as the icon is also different for it vs what I was expecting.

image

vobu commented 5 years ago

Hi,

Just came across this problem after upgrading to Mojave on my work laptop. I am having the exact same error message output as @vobu after modifying karma-safari-launcher/index.js @muthu90ec - when i CD into Users/ring/Library/Containers/com.apple.Safari/Data, I'm not able to perform any

cmd: touch test.txt out: Operation not permitted cmd: ls out: Operation not permitted

Even when I sudo, nothing works. However, when in the Finder if I manually go into /Data, and do Get Info, it says I have read/write access. Additionally, I can drag files in there with no problem.

@vobu did you ever figure this out?

unfortunately no; the issue got pushed back on my todo list, but it's still on there. Haven't had time to revisit yet. Am sharing your assumption that @muthu90ec gets his home dir served per AFP/NFS in a corporate environment; as opposed to us 2 using our macs in "standalone" mode. @gkatsev you seem to have merged a fix regarding this issue; however I couldn't find a new version of karma-safari-launcher on npm (yet)...anything coming up or you need a hand with?

jeffsaremi commented 5 years ago

The same issue happens on my machine. Is there a fix for this?

Safari 12.0.1 Mac OS X 10.14.1 karma-safari-laucnher 1.0.0

vobu commented 5 years ago

I figured out a workaround: when using karma-safari-private-launcher https://www.npmjs.com/package/karma-safari-private-launcher, Safari doesn't open the File Popup. The tests via karma then ran fine. But: upon first launch I had to both allow the launching instance (WebStorm in my case) to control the computer (in preferences) and confirm the privileges pop-up. After that, happy Safari-karma test runs here.

vobu commented 5 years ago

Just tried it out and it seems to be working for me.

I just made the change here and in the STP launcher

// const staticHtmlPath = self._tempDir + '/redirect.html';
var staticHtmlPath = path.join(process.env.HOME, 'Library/Containers/com.apple.Safari/Data/redirect.html');
// const staticHtmlPath = self._tempDir + '/redirect.html';
var staticHtmlPath = path.join(process.env.HOME, 'Library/Containers/com.apple.SafariTechnologyPreview/Data/redirect.html');

this unfortunately never worked for me. I'm on a fresh install of Mojave. And it bugged me to the extent that I filed a bug report: https://bugreport.apple.com/web/?problemID=46549321

vobu commented 5 years ago

And it bugged me to the extent that I filed a bug report: https://bugreport.apple.com/web/?problemID=46549321

for $whoever is interested: Apple closed the bug and says "feature":

This appears to be a System Integrity Protection issue. In macOS 10.14 system-integrity-protection prevents creating files in ~/Library/Safari. It is expected behavior. Weโ€™re sorry this took so long to diagnose.

Doesn't explain, why ~/Library/Containers/com.apple.Safari** isn't accessible. I commented the closed bug accordingly, hoping to receive some more insight from Apple support. So we'll see.

muthu90ec commented 5 years ago

And it bugged me to the extent that I filed a bug report: https://bugreport.apple.com/web/?problemID=46549321

for $whoever is interested: Apple closed the bug and says "feature":

This appears to be a System Integrity Protection issue. In macOS 10.14 system-integrity-protection prevents creating files in ~/Library/Safari. It is expected behavior. Weโ€™re sorry this took so long to diagnose.

Doesn't explain, why ~/Library/Containers/com.apple.Safari** isn't accessible. I commented the closed bug accordingly, hoping to receive some more insight from Apple support. So we'll see.

Thanks. There is another way to work around this problem. We can use MAC OS's native apis (Launch services) to open urls in Safari browser. https://developer.apple.com/documentation/coreservices/lslaunchurlspec https://developer.apple.com/documentation/coreservices/1441986-lsopenfromurlspec

But this will require a binary to be built first. Here is a swift code which will accept a url as a command line argument. And will load the url in Safari even if the default web browser is set to something else.

import Foundation

let args = CommandLine.arguments
let argCount = CommandLine.argc
if (argCount != 2) {
    print("The safari url launcher app needs exactly one argument. Exiting with code: 1")
    exit(1)
}
let userUrl = args[1]
let url = URL(string: userUrl)
let safariString = CFStringCreateCopy(nil, "Safari" as CFString)

let safariApp = URL(string: "file:///Applications/Safari.app" ) as CFURL?

let safariUnManagedURL = Unmanaged<CFURL>.passUnretained(safariApp!)

let userURLs: CFArray = [url] as CFArray
let unManagedUserURLs = Unmanaged<CFArray>.passUnretained(userURLs)

var launchSpec = LSLaunchURLSpec(appURL: safariUnManagedURL, itemURLs: unManagedUserURLs, passThruParams: nil, launchFlags: LSLaunchFlags.newInstance, asyncRefCon: nil)

let stat = LSOpenFromURLSpec(&launchSpec, nil)
if (stat == 0) {
    print("success:", stat)
    exit(0)
} else {
    print("error:", stat)
    exit(1)
}

Quick patch to test the binary out:

//safariUrlLauncher is the built binary using above code
//The url is the karma server's url and not the redirect.html url
require('child_process').spawnSync(safariUrlLauncher, [url], {"encoding" : "utf8", "cwd": os.tmpdir()});
vobu commented 5 years ago

Thanks. There is another way to work around this problem. We can use MAC OS's native apis (Launch services) to open urls in Safari browser. https://developer.apple.com/documentation/coreservices/lslaunchurlspec https://developer.apple.com/documentation/coreservices/1441986-lsopenfromurlspec

But this will require a binary to be built first. Here is a swift code which will accept a url as a command line argument. And will load the url in Safari even if the default web browser is set to something else.

nice idea! does this allow handing require('child_process').spawnSync(...) as module.exports = {...} just like https://github.com/karma-runner/karma-safari-launcher/blob/master/index.js?

Would be cool if you could do a feature-branch on this. I'd be on board for testing.

muthu90ec commented 5 years ago

Thanks. There is another way to work around this problem. We can use MAC OS's native apis (Launch services) to open urls in Safari browser. https://developer.apple.com/documentation/coreservices/lslaunchurlspec https://developer.apple.com/documentation/coreservices/1441986-lsopenfromurlspec But this will require a binary to be built first. Here is a swift code which will accept a url as a command line argument. And will load the url in Safari even if the default web browser is set to something else.

nice idea! does this allow handing require('child_process').spawnSync(...) as module.exports = {...} just like https://github.com/karma-runner/karma-safari-launcher/blob/master/index.js?

Would be cool if you could do a feature-branch on this. I'd be on board for testing.

Hi, Please clone this repo and try using using in your node_modules, https://github.com/muthu90ec/karma-safarinative-launcher

For easy access created npm package: https://www.npmjs.com/package/karma-safarinative-launcher

I tested it on my machine and it works

vobu commented 5 years ago

And it bugged me to the extent that I filed a bug report: https://bugreport.apple.com/web/?problemID=46549321

for $whoever is interested: Apple closed the bug and says "feature":

This appears to be a System Integrity Protection issue. In macOS 10.14 system-integrity-protection prevents creating files in ~/Library/Safari. It is expected behavior. Weโ€™re sorry this took so long to diagnose.

Doesn't explain, why ~/Library/Containers/com.apple.Safari** isn't accessible. I commented the closed bug accordingly, hoping to receive some more insight from Apple support. So we'll see.

FTR: Apple support finally stated in https://bugreport.apple.com/web/?problemID=46549321:

Yes, system protection applies to ~/Library/Containers as well.

So there's that. Now it's either using https://www.npmjs.com/package/karma-safari-private-launcher or following up with @muthu90ec's proposed solution (which I haven't had the time to test yet, but will).

3cp commented 5 years ago

There is another way to supply a url to safari without using an html file to redirect.

open -F -W -n -b com.apple.Safari https://www.kernel.org

The problem is I now got the process pid of open command, not the Safari process itself. So I can only programmerly kill the open process, but safari process is still running.

brandonocasey commented 5 years ago

I created a workaround for this via a new launcher that uses applescript. https://www.npmjs.com/package/karma-safari-applescript-launcher

jdmarshall commented 3 years ago

Is there a reason for trampolining safari through an html file instead of passing a url to it?

heidemn-faro commented 3 years ago

In Azure Pipelines, using a Microsoft macOS-10.15 agent, these are my results:

For the applescript launcher, I get this error:

29 06 2021 08:16:19.847:INFO [karma-server]: Karma v5.1.0 server started at http://0.0.0.0:9876/
29 06 2021 08:16:19.856:INFO [launcher]: Launching browsers Safari with concurrency unlimited
29 06 2021 08:16:19.874:INFO [launcher]: Starting browser Safari
29 06 2021 08:18:19.912:WARN [launcher]: Safari have not captured in 120000 ms, killing.
29 06 2021 08:18:24.672:ERROR [karma-server]: UnhandledRejection: Command failed: osascript -e 
    set wasopen to false
    if application "Safari" is running then set wasopen to true
    tell application "Safari"
      make new document with properties {URL:"http://localhost:9876/?id=39841744"}
    end tell
    return wasopen

118:203: execution error: Safari got an error: AppleEvent timed out. (-1712)

29 06 2021 08:18:24.674:ERROR [karma-server]: Error: Command failed: osascript -e 
    set wasopen to false
    if application "Safari" is running then set wasopen to true
    tell application "Safari"
      make new document with properties {URL:"http://localhost:9876/?id=39841744"}
    end tell
    return wasopen

118:203: execution error: Safari got an error: AppleEvent timed out. (-1712)

    at makeError (/Users/runner/work/1/s/3_tools/node_modules/execa/index.js:172:9)
    at /Users/runner/work/1/s/3_tools/node_modules/execa/index.js:277:16
    at processTicksAndRejections (internal/process/task_queues.js:95:5) {
  code: 1,
  stdout: '',
  stderr: '118:203: execution error: Safari got an error: AppleEvent timed out. (-1712)\n',
  failed: true,
  signal: null,
  cmd: 'osascript -e \n' +
    '    set wasopen to false\n' +
    '    if application "Safari" is running then set wasopen to true\n' +
    '    tell application "Safari"\n' +
    '      make new document with properties {URL:"http://localhost:9876/?id=39841744"}\n' +
    '    end tell\n' +
    '    return wasopen\n' +
    '    ',
  timedOut: false,
  killed: false
}
>> Tests failed with exit code 1
(node:2236) UnhandledPromiseRejectionWarning: Error: Command failed: osascript -e 
    tell application "Safari"
      close documents where URL = "http://localhost:9876/?id=39841744"
      quit
    end tell

37:101: execution error: Safari got an error: AppleEvent timed out. (-1712)

   at makeError (/Users/runner/work/1/s/3_tools/node_modules/execa/index.js:172:9)
    at /Users/runner/work/1/s/3_tools/node_modules/execa/index.js:277:16
    at processTicksAndRejections (internal/process/task_queues.js:95:5)
(Use `node --trace-warnings ...` to show where the warning was created)
(node:2236) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 4)
(node:2236) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
fosemberg commented 2 years ago

The same issue happens on my machine.

macOS: Monterey 12.2.1 (21D62) Safari: 15.3 (17612.4.9.1.8) karma-safari-launcher: 1.0.0

jdmarshall commented 2 years ago

It's been a while, but I fixed this on my project by modifying how safari opens so it doesn't use a trampoline html file and just opens Safari directly.

Is there a reason for trampolining safari through an html file instead of passing a url to it?