userscripts icon indicating copy to clipboard operation
userscripts copied to clipboard

GM_setClipboard not always copying to clipboard on iOS

Open jian263994241 opened this issue 3 years ago • 10 comments

System Information:

macOS or iOS version: iOS 15.5 / 16b3 Userscripts version: latest Safari version: Is this issue related to script injection? Did the test script (pasted above) successfully run on your machine? yes

jian263994241 avatar Jul 07 '22 07:07 jian263994241

@jian263994241

Please provide an example of a userscript using GM_setClipboard that is failing

quoid avatar Jul 07 '22 14:07 quoid

@quoid

// ==UserScript==
// @name         magnet_copy
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       jxl
// @match        *://*/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=baidu.com
// @grant        GM_setClipboard
// ==/UserScript==

const toLocaleLowerCase = (string) =>
  String.prototype.toLocaleLowerCase.call(string);

(function () {
  'use strict';

  document.addEventListener('click', (e) => {
    const currentNode = e.target;
    const tagName = toLocaleLowerCase(currentNode.tagName);
    if (
      tagName === 'a' &&
      currentNode.href &&
      currentNode.href.indexOf('magnet:?xt=urn') > -1
    ) {
      e.preventDefault();
      GM_setClipboard(currentNode.href);
    }
  });
})();


jian263994241 avatar Jul 08 '22 01:07 jian263994241

@jian263994241

This seems like a problem with your javascript code, not the GM_setClipboard method.

I put a simplified example below. Any element you click will have the tag name copied to the clipboard. If you are not seeing the tagName copied, let me know, but it works for me on multiple machines.

// ==UserScript==
// @name         magnet_copy2
// @version      0.2
// @description  try to take over the world!
// @match        *://*/*
// @grant        GM_setClipboard
// ==/UserScript==

const toLocaleLowerCase = (string) =>
  String.prototype.toLocaleLowerCase.call(string);

(function () {
  'use strict';

  document.addEventListener('click', (e) => {
    const currentNode = e.target;
    const tagName = toLocaleLowerCase(currentNode.tagName);
    GM_setClipboard(tagName);
  });
})();

quoid avatar Jul 08 '22 02:07 quoid

@quoid it does not work on iOS safari (iphone/ipad)

jian263994241 avatar Jul 08 '22 07:07 jian263994241

@jian263994241 Sorry about that, I missed where you mentioned iOS above.

This doesn't always work for me on iOS either, so there is definitely an issue, although it did work sometimes.

I am noticing that the execCommand is returning false a lot on iOS

quoid avatar Jul 08 '22 13:07 quoid

@quoid Maybe try the new API interface: https://developer.mozilla.org/en-US/docs/Web/API/Clipboard

Clipboard API

This API is designed to supersede accessing the clipboard using document.execCommand()

Document.execCommand() has been marked as deprecated according to its documentation

Deprecated: This feature is no longer recommended. Though some browsers might still support it, it may have already been removed from the relevant web standards, may be in the process of being dropped, or may only be kept for compatibility purposes. Avoid using it, and update existing code if possible; see the compatibility table at the bottom of this page to guide your decision. Be aware that this feature may cease to work at any time.

ACTCD avatar Jul 08 '22 19:07 ACTCD

I did some more testing for this on an iPhone 8 running the script pasted below. The script just copies the tagName of the element that is clicked. When I navigate to the k21p website, I am clicking on an element, although you can not see click actions in the video.

The video demonstrates that the setClipboard method works (at least sometimes), however I did notice that sometimes it's not very quick. On some there was a noticeable delay between clicking on the element and having the data written to the clipboard. The way I could determine this is because I was monitoring the events on my laptop through the iPhone dev tools.

I am not sure if the performance issues mentioned above reflected new iPhone, I only have an 8 to test on.

I will have to keep testing this and see if I can determine why it seems to fail sometimes. It would also be possible to refactor the method so that instead of relying on Web APIs, a message could sent to the swift side and set the clipboard contents there, but I am trying to get away from that methodology as much as possible.

It's also important to note that the current implementation for setting clipboard relies on a "no longer recommended" API method - this is the same method i have seen other open source userscript managers use and, from what I can see, is the generally user-recommended way for programmatically setting the clipboard.


@ACTCD I am not sure that would be viable consdering the implementation of that API in Webkit:

The implementation is available through the navigator.clipboard API which must be called within user gesture event handlers like pointerdown or pointerup, and only works for content served in a secure context (e.g. https://).

I've tried it and errors get thrown when trying to programmatically set clipboard data (ie. no user gesture).


// ==UserScript==
// @name         magnet_copy2
// @version      0.2
// @description  try to take over the world!
// @match        *://*/*
// @grant        GM.setClipboard
// @grant        GM_setClipboard
// ==/UserScript==

const toLocaleLowerCase = (string) =>
  String.prototype.toLocaleLowerCase.call(string);

(function () {
  'use strict';

  document.addEventListener('click', (e) => {
    const currentNode = e.target;
    const tagName = toLocaleLowerCase(currentNode.tagName);
    GM.setClipboard(tagName);
    //GM_setClipboard(tagName);
    console.log(tagName);
  });
})();

https://user-images.githubusercontent.com/7660254/178070406-fa899e5c-9f50-494f-83aa-9a5658858268.MP4

quoid avatar Jul 08 '22 21:07 quoid

@quoid

I've tried it and errors get thrown when trying to programmatically set clipboard data (ie. no user gesture).

I think it will be a trend to block read and write clipboards for non-user active actions.

In the iOS 16 beta, an authorization dialog will pop up to let the user choose whether to allow the app to read the clipboard. Before that, it just displayed a prompt at the top.

There are many web pages where scripts maliciously write and edit the clipboard, so that's probably why there must be user interaction.

If the user script needs to edit the clipboard without interaction, perhaps the design of the script function should be reconsidered.

ACTCD avatar Jul 09 '22 01:07 ACTCD

@ACTCD Yep, agreed. Programmatically allowing users to access the clipboard is a security risk, even just writing.

As mentioned above, all of the open-source implementations I have seen, use the execCommand method, which really is a hacky implementation. Once that gets depreciated, I am not sure what other alternatives there will be (outside of the swift method I mentioned above). But I am not sure if it is even a good idea to have this GM method, unscrupulous userscript editors could do a lot of damage to users who don't audit the code they are running on their machines.

  • https://github.com/violentmonkey/violentmonkey/blob/master/src/background/utils/clipboard.js
  • https://github.com/quoid/userscripts/blob/2a83cf0b62932077d0019df5f704c35725f0fa4a/extension/Userscripts%20Extension/Resources/background.js#L430

quoid avatar Jul 09 '22 01:07 quoid

@quoid

I looked at the official description of this function and it doesn't seem to stress that this must be implemented programmatically, it looks like it just exists as an alias.

In fact I have never used this GM function, I just simply used the native js function and it worked fine for my use case.

navigator.clipboard.writeText('text');

I don't think an iOS app can write to the clipboard when it's not in the foreground, but I'm not sure if an extension app can do this (the swift method you mentioned). I tend to think this is another hacky implementation if it works, it may end up being blocked by Apple.

ACTCD avatar Jul 09 '22 13:07 ACTCD