userscripts icon indicating copy to clipboard operation
userscripts copied to clipboard

Ability to close a tab is limited to javascript-opened or "⌘-click" opened

Open TS6ix2GaH3q679h5 opened this issue 3 years ago • 8 comments
trafficstars

I'd like to have a script that lets me close the currently open tab when I click in a specific part of the page.

The problem is, using

close();

in a script is unreliable. It can only close tabs that either were opened by Javascript to begin with, or tabs that I manually "Open in new tab" opened, be it from the context menu or a ⌘-click action.

I've tried adding the following:

// @grant        window.close
// @grant      close
// @grant       GM_addStyle

But no luck there either. As I understand it, this is a security issue that doesn't allow page scripts to close tabs. Indeed, when I run my user script, the permissions seem ok:

close tab test 3.js @inject-into value changed due to @grant values

But when I try it in standard Safari tabs, console logs:

Can't close the window since it was not opened by JavaScript

Is there some way the Userscript extension can use it's browser extension status, existing as it does "above" any page scripts, to permit this behavior? A Chrome extension I also use "Close this Tab" can do something very similar. It's likely because of it's privileges as an extension rather than just a user script. I'd love to be able to use that close command on any active tab.

Thank you

System Information:

macOS or iOS version: macOS 12.5 Userscripts version: 4.2.3 Safari version: 15.6 Is this issue related to script injection? Sort of Did the test script (pasted above) successfully run on your machine? Yes

TS6ix2GaH3q679h5 avatar Aug 31 '22 15:08 TS6ix2GaH3q679h5

@TS6ix2GaH3q679h5

Have you tried the API method US.closeTab(tabId)? You need to @grant like the other APIs, @grant US.closeTab

- `US.closeTab(tabId)`
    - `tabId: Int`
    - `tabId` is **optional** and if omitted the tab that called `US.closeTab` will be closed
    - on success returns a promise resolved with an object indicating succes

quoid avatar Aug 31 '22 16:08 quoid

If it's just a matter of adding the line @grant US.closeTab to user script, then I'm afraid it doesn't work. I've got the metadata like this:

// ==UserScript==
// @name        close tab test 3
// @description Should close tabs on double click
// @match       *://*/*
// @grant        window.close
// @grant      close
// @grant       GM_addStyle
// @grant       US.closeTab
// ==/UserScript==

But still getting the Can't close the window since it was not opened by JavaScript in console. Works fine for any tab opened with ⌘-click, but can't close any tabs opened by ⌘-T or by a link from outside Safari.

TS6ix2GaH3q679h5 avatar Aug 31 '22 16:08 TS6ix2GaH3q679h5

@TS6ix2GaH3q679h5

You have to call the API method to close the tab

function closeTab() {
  US.closeTab();
}

If you want to rewrite the window.close() function, you can do that too.

quoid avatar Aug 31 '22 17:08 quoid

Oh boy. Seems like I'm not javascript-savvy enough to figure out how to replace the close(); in my script with the correct code then.

But that means this has become a problem with my script and apparently not an issue with the extension itself. I guess we can go ahead and close this issue then.

TS6ix2GaH3q679h5 avatar Aug 31 '22 17:08 TS6ix2GaH3q679h5

Seems like I'm not javascript-savvy enough to figure out how to replace the close(); in my script with the correct code then.

Anywhere you are using close(); just replace it with US.closeTab(); (assuming you are closing the current tab).

quoid avatar Aug 31 '22 18:08 quoid

Tried that, but I end up with this console error:

ReferenceError: Can't find variable: US_closeTab

Same if I use US.closeTab(); too. Here's my entire script, pieced together from copy/pasting other code. It worked in the situations I described (Command-click opened tabs, etc) with standard close(); there. I've replaced it with your suggestion. Seems like I need to define it but I'm not sure how.

// ==UserScript==
// @name        close tab by doubleclick test 4
// @description Should close tabs when sidebar is double clicked
// @match       *://*/*
// @grant       US.closeTab
// @grant        window.close
// @grant        close
// @grant       GM_addStyle
// ==/UserScript==

var style = document.createElement('style');
  style.innerHTML = `
  body {
  border-left: solid 40px darkred;
  }
  `;
  document.head.appendChild(style);

document.addEventListener("dblclick",function (event) { 
        if(event.clientX < 40 ) {
        US.closeTab();
        }
});

Sorry to take up your time with this. But I really appreciate any help.

TS6ix2GaH3q679h5 avatar Aug 31 '22 23:08 TS6ix2GaH3q679h5

@TS6ix2GaH3q679h5

Thanks for following up.

Sorry to take up your time with this. But I really appreciate any help.

Absolutely no need to apologize! After investigating this more, I found a bug which is why the method is not working.

The API method is not being properly passed to the userscript, that's why you see that error. However, I just pushed a fix to the version 4.2.4 branch and will be pushing it to the a beta build.

If you want, you can sign up for the beta builds to test it (links in readme)

Thank you again for bringing this up! the bug completely went overlooked!

quoid avatar Sep 01 '22 00:09 quoid

Great to know. I've never used TestFlight before so I might just wait until any potential fixes reach the stable version, but I will be sure to check and verify there if it works!

TS6ix2GaH3q679h5 avatar Sep 06 '22 05:09 TS6ix2GaH3q679h5

resolved in https://github.com/quoid/userscripts/commit/38b4e08c4fbf3b29311d2241943b24c994e2c567

quoid avatar Oct 11 '22 14:10 quoid

Just an update on this: I'm not sure the issue has been solved. I still can't get the functionality working.

Here's what I've tried:

// ==UserScript==
// @name        close tab by doubleclick test 555
// @description Should close tabs when sidebar is double clicked
// @match       *://*/*
// @grant       US.closeTab
// @grant       .closeTab
// @grant        window.close
// @grant        close
// @grant       GM_addStyle
// ==/UserScript==

var style = document.createElement('style');
  style.innerHTML = `
  body {
  border-left: solid 40px darkred;
  }
  `;
  document.head.appendChild(style);

document.addEventListener("dblclick",function (event) { 
        if(event.clientX < 40 ) {
        window.close();
        }
});

With that final window.close() the console returns Can't close the window since it was not opened by JavaScript

If I change it to US.closeTab() the console returns [Error] ReferenceError: Can't find variable: US(anonymous function) (close-tab-by-doubleclick-test-555.js:19)

I've sort of given up hope that this functionality will be possible. Is it actually working for anyone else?

TS6ix2GaH3q679h5 avatar Jan 20 '23 01:01 TS6ix2GaH3q679h5

@TS6ix2GaH3q679h5

You shouldn't be trying to call window.close() unless you've programmatically opened the window you are trying to close.

US.closeTab()

The method was renamed to GM.closeTab which may be the source of your issue.

The below script works perfectly for me. Install it and visit https://www.example.com and after two seconds the window closes.

// ==UserScript==
// @name        NewScript-mw5zz81t
// @description This is your new file, start writing code
// @match       *://*.example.com/*
// @grant GM.closeTab
// ==/UserScript==


(async () => {
    await new Promise(resolve => setTimeout(() => resolve(), 2000))
    GM.closeTab();
})();

quoid avatar Jan 20 '23 03:01 quoid

Bingo! I just replaced the @grant US.closeTab and the US.closeTab() with their GM.closeTab counterparts and bingo, my original script is working for the first time ever. I'm very pleased. Thank you greatly for your assistance and support.

TS6ix2GaH3q679h5 avatar Jan 20 '23 06:01 TS6ix2GaH3q679h5